X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=a4af694661959a351b18b3cd99d2ba81672804d0;hb=5637bdd29b91b8953bf0b064364ade2d890869c6;hp=a449540c6fbf82e64b5bc1d0e910c73f0f58fb54;hpb=f485cfa324717f57b9f820f43f1b53307b96a8b9;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index a449540c6f..a4af694661 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -30,9 +30,10 @@ #include "pbd/xml++.h" #include "pbd/enumwriter.h" +#include "pbd/locale_guard.h" #include "pbd/memento_command.h" #include "pbd/stacktrace.h" -#include "pbd/convert.h" +#include "pbd/types_convert.h" #include "pbd/unwind.h" #include "ardour/amp.h" @@ -46,6 +47,7 @@ #include "ardour/capturing_processor.h" #include "ardour/debug.h" #include "ardour/delivery.h" +#include "ardour/event_type_map.h" #include "ardour/gain_control.h" #include "ardour/internal_return.h" #include "ardour/internal_send.h" @@ -70,26 +72,24 @@ #include "ardour/session.h" #include "ardour/solo_control.h" #include "ardour/solo_isolate_control.h" +#include "ardour/types_convert.h" #include "ardour/unknown_processor.h" #include "ardour/utils.h" #include "ardour/vca.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace std; using namespace ARDOUR; using namespace PBD; -PBD::Signal0 Route::SyncOrderKeys; -PBD::Signal0 Route::RemoteControlIDChange; PBD::Signal3, boost::shared_ptr, 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) - : GraphNode (sess._process_graph) - , Stripable (sess, name) +Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType default_type) + : Stripable (sess, name, PresentationInfo (flag)) + , GraphNode (sess._process_graph) , Muteable (sess, name) - , Automatable (sess) , _active (true) , _signal_latency (0) , _signal_latency_at_amp_position (0) @@ -98,7 +98,6 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _roll_delay (0) , _pending_process_reorder (0) , _pending_signals (0) - , _flags (flg) , _pending_declick (true) , _meter_point (MeterPostFader) , _pending_meter_point (MeterPostFader) @@ -109,9 +108,6 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _declickable (false) , _have_internal_generator (false) , _default_type (default_type) - , _order_key (0) - , _has_order_key (false) - , _remote_control_id (0) , _track_number (0) , _in_configure_processors (false) , _initial_io_setup (false) @@ -119,10 +115,16 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _strict_io (false) , _custom_meter_position_noted (false) , _pinmgr_proxy (0) + , _patch_selector_dialog (0) { processor_max_streams.reset(); } +boost::weak_ptr +Route::weakroute () { + return boost::weak_ptr (boost::dynamic_pointer_cast (shared_from_this ())); +} + int Route::init () { @@ -139,10 +141,12 @@ Route::init () /* add standard controls */ _gain_control.reset (new GainControl (_session, GainAutomation)); - add_control (_gain_control); - _trim_control.reset (new GainControl (_session, TrimAutomation)); - add_control (_trim_control); + /* While the route has-a gain-control for consistency with Stripable and VCA + * ownership is handed over to the Amp Processor which manages the + * state of the Control and AutomationList as part of its + * Automatable API. -- Don't call add_control () here. + */ _solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this)); add_control (_solo_control); @@ -162,7 +166,7 @@ Route::init () /* panning */ - if (!(_flags & Route::MonitorOut)) { + if (!(_presentation_info.flags() & PresentationInfo::MonitorOut)) { _pannable.reset (new Pannable (_session)); } @@ -235,10 +239,6 @@ Route::init () _monitor_control->activate (); } - if (is_master() || is_monitor() || is_auditioner()) { - _mute_master->set_solo_ignore (true); - } - /* now that we have _meter, its safe to connect to this */ { @@ -270,120 +270,6 @@ Route::~Route () _processors.clear (); } -void -Route::set_remote_control_id (uint32_t id, bool notify_class_listeners) -{ - if (Config->get_remote_model() != UserOrdered) { - return; - } - - set_remote_control_id_internal (id, notify_class_listeners); -} - -void -Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners) -{ - /* force IDs for master/monitor busses and prevent - any other route from accidentally getting these IDs - (i.e. legacy sessions) - */ - - if (is_master() && id != MasterBusRemoteControlID) { - id = MasterBusRemoteControlID; - } - - if (is_monitor() && id != MonitorBusRemoteControlID) { - id = MonitorBusRemoteControlID; - } - - if (id < 1) { - return; - } - - /* don't allow it to collide */ - - if (!is_master () && !is_monitor() && - (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) { - id += MonitorBusRemoteControlID; - } - - if (id != remote_control_id()) { - _remote_control_id = id; - RemoteControlIDChanged (); - - if (notify_class_listeners) { - RemoteControlIDChange (); - } - } -} - -uint32_t -Route::remote_control_id() const -{ - if (is_master()) { - return MasterBusRemoteControlID; - } - - if (is_monitor()) { - return MonitorBusRemoteControlID; - } - - return _remote_control_id; -} - -bool -Route::has_order_key () const -{ - return _has_order_key; -} - -uint32_t -Route::order_key () const -{ - return _order_key; -} - -void -Route::set_remote_control_id_explicit (uint32_t rid) -{ - if (is_master() || is_monitor() || is_auditioner()) { - /* hard-coded remote IDs, or no remote ID */ - return; - } - - if (_remote_control_id != rid) { - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1: set edit-based RID to %2\n", name(), rid)); - _remote_control_id = rid; - RemoteControlIDChanged (); /* EMIT SIGNAL (per-route) */ - } - - /* don't emit the class-level RID signal RemoteControlIDChange here, - leave that to the entity that changed the order key, so that we - don't get lots of emissions for no good reasons (e.g. when changing - all route order keys). - - See Session::sync_remote_id_from_order_keys() for the (primary|only) - spot where that is emitted. - */ -} - -void -Route::set_order_key (uint32_t n) -{ - _has_order_key = true; - - if (_order_key == n) { - return; - } - - _order_key = n; - - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 order key set to %2\n", - name(), order_key ())); - - _session.set_dirty (); -} - string Route::ensure_track_or_route_name(string name, Session &session) { @@ -437,6 +323,8 @@ Route::process_output_buffers (BufferSet& bufs, return; } + automation_run (start_frame, nframes); + /* figure out if we're going to use gain automation */ if (gain_automation_ok) { _amp->set_gain_automation_buffer (_session.gain_automation_buffer ()); @@ -533,6 +421,7 @@ Route::process_output_buffers (BufferSet& bufs, bool const meter_already_run = metering_state() == MeteringInput; framecnt_t latency = 0; + const double speed = _session.transport_speed (); for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { @@ -562,8 +451,13 @@ Route::process_output_buffers (BufferSet& bufs, if (boost::dynamic_pointer_cast(*i) != 0) { boost::dynamic_pointer_cast(*i)->set_delay_in(_signal_latency - latency); } + if (boost::dynamic_pointer_cast(*i) != 0) { + const framecnt_t longest_session_latency = _initial_delay + _signal_latency; + boost::dynamic_pointer_cast(*i)->set_sidechain_latency ( + _initial_delay + latency, longest_session_latency - latency); + } - (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back()); + (*i)->run (bufs, start_frame - latency, end_frame - latency, speed, nframes, *i != _processors.back()); bufs.set_count ((*i)->output_streams()); if ((*i)->active ()) { @@ -591,6 +485,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, _trim->setup_gain_automation (start, start + nframes, nframes); latency = 0; + const double speed = _session.transport_speed (); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (!include_endpoint && (*i) == endpoint) { @@ -613,7 +508,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, */ if ((*i) == _main_outs) { assert ((*i)->does_routing()); - (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true); + (*i)->run (buffers, start - latency, start - latency + nframes, speed, nframes, true); buffers.set_count ((*i)->output_streams()); } @@ -621,7 +516,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, * Also don't bother with metering. */ if (!(*i)->does_routing() && !boost::dynamic_pointer_cast(*i)) { - (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true); + (*i)->run (buffers, start - latency, start - latency + nframes, 1.0, nframes, true); buffers.set_count ((*i)->output_streams()); latency += (*i)->signal_latency (); } @@ -737,6 +632,9 @@ void Route::set_listen (bool yn) { if (_monitor_send) { + if (_monitor_send->active() == yn) { + return; + } if (yn) { _monitor_send->activate (); } else { @@ -946,6 +844,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "windows-vst" || + prop->value() == "mac-vst" || prop->value() == "lxvst" || prop->value() == "audiounit") { @@ -981,7 +880,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) //A2 uses the "active" flag in the toplevel redirect node, not in the child plugin/IO if (i != children.end()) { if ((prop = (*i)->property (X_("active"))) != 0) { - if ( string_is_affirmative (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) ) + if ( string_to (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) ) processor->activate(); else processor->deactivate(); @@ -1010,6 +909,13 @@ int Route::add_processors (const ProcessorList& others, boost::shared_ptr before, ProcessorStreams* err) { ProcessorList::iterator loc; + boost::shared_ptr fanout; + + if (g_atomic_int_get (&_pending_process_reorder)) { + /* we need to flush any pending re-order changes */ + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + apply_processor_changes_rt (); + } if (before) { loc = find(_processors.begin(), _processors.end(), before); @@ -1021,10 +927,6 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr loc = _processors.end (); } - if (!AudioEngine::instance()->connected()) { - return 1; - } - if (others.empty()) { return 0; } @@ -1067,8 +969,9 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr flags &= mask; if (flags != None) { - boost::optional rv = PluginSetup (shared_from_this (), pi, flags); /* EMIT SIGNAL */ - switch (rv.get_value_or (0)) { + boost::optional rv = PluginSetup (boost::dynamic_pointer_cast(shared_from_this ()), pi, flags); /* EMIT SIGNAL */ + int mode = rv.get_value_or (0); + switch (mode & 3) { case 1: to_skip.push_back (*i); // don't add this one; break; @@ -1079,6 +982,9 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr default: break; } + if ((mode & 5) == 4) { + fanout = pi; + } } } @@ -1129,7 +1035,12 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr } } + if (pi && pi->has_sidechain ()) { + pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2)); + } + if ((*i)->active()) { + // emit ActiveChanged() and latency_changed() if needed (*i)->activate (); } @@ -1160,6 +1071,11 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ set_processor_positions (); + if (fanout && fanout->configured () + && fanout->output_streams().n_audio() > 2 + && boost::dynamic_pointer_cast (the_instrument ()) == fanout) { + fan_out (); /* EMIT SIGNAL */ + } return 0; } @@ -1188,7 +1104,7 @@ Route::disable_processors (Placement p) placement_range(p, start, end); for (ProcessorList::iterator i = start; i != end; ++i) { - (*i)->deactivate (); + (*i)->enable (false); } _session.set_dirty (); @@ -1202,7 +1118,7 @@ Route::disable_processors () Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->deactivate (); + (*i)->enable (false); } _session.set_dirty (); @@ -1221,7 +1137,7 @@ Route::disable_plugins (Placement p) for (ProcessorList::iterator i = start; i != end; ++i) { if (boost::dynamic_pointer_cast (*i)) { - (*i)->deactivate (); + (*i)->enable (false); } } @@ -1237,7 +1153,7 @@ Route::disable_plugins () for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { - (*i)->deactivate (); + (*i)->enable (false); } } @@ -1260,9 +1176,17 @@ Route::ab_plugins (bool forward) if (!boost::dynamic_pointer_cast (*i)) { continue; } + if (!(*i)->display_to_user ()) { + continue; + } +#ifdef MIXBUS + if (boost::dynamic_pointer_cast (*i)->is_channelstrip()) { + continue; + } +#endif - if ((*i)->active()) { - (*i)->deactivate (); + if ((*i)->enabled ()) { + (*i)->enable (false); (*i)->set_next_ab_is_active (true); } else { (*i)->set_next_ab_is_active (false); @@ -1274,16 +1198,19 @@ Route::ab_plugins (bool forward) /* backward = if the redirect was marked to go active on the next ab, do so */ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (!boost::dynamic_pointer_cast (*i)) { continue; } - - if ((*i)->get_next_ab_is_active()) { - (*i)->activate (); - } else { - (*i)->deactivate (); + if (!(*i)->display_to_user ()) { + continue; } +#ifdef MIXBUS + if (boost::dynamic_pointer_cast (*i)->is_channelstrip()) { + continue; + } +#endif + + (*i)->enable ((*i)->get_next_ab_is_active ()); } } @@ -1306,6 +1233,7 @@ Route::clear_processors (Placement p) _session.set_deletion_in_progress(); } + ProcessorList old_list = _processors; { Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); @@ -1354,6 +1282,8 @@ Route::clear_processors (Placement p) _processors = new_list; configure_processors_unlocked (&err, &lm); // this can't fail } + /* drop references w/o process-lock (I/O procs may re-take it in ~IO() */ + old_list.clear (); processor_max_streams.reset(); _have_internal_generator = false; @@ -1524,7 +1454,7 @@ Route::replace_processor (boost::shared_ptr old, boost::shared_ptractive (); + bool enable = old->enabled (); for (i = _processors.begin(); i != _processors.end(); ) { if (*i == old) { @@ -1568,7 +1498,7 @@ Route::replace_processor (boost::shared_ptr old, boost::shared_ptractivate (); + sub->enable (true); } sub->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false)); @@ -1737,8 +1667,8 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) if (boost::dynamic_pointer_cast (*p) && boost::dynamic_pointer_cast (*p)->role() == Delivery::Main - && !(is_monitor() || is_auditioner()) - && ( _strict_io || Profile->get_mixbus ())) { + && !is_auditioner() + && (is_monitor() || _strict_io || Profile->get_mixbus ())) { /* with strict I/O the panner + output are forced to * follow the last processor's output. * @@ -1750,8 +1680,9 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) * Delivery::configure_io() will do the actual removal * by calling _output->ensure_io() */ - if (!is_master() && _session.master_out ()) { - /* ..but at least as many as there are master-inputs */ + if (!is_master() && _session.master_out () && in.n_audio() > 0) { + /* ..but at least as many as there are master-inputs, if + * the delivery is dealing with audio */ // XXX this may need special-casing for mixbus (master-outputs) // and should maybe be a preference anyway ?! out = ChanCount::max (in, _session.master_out ()->n_inputs ()); @@ -1766,13 +1697,21 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) if (is_monitor()) { // restriction for Monitor Section Processors if (in.n_audio() != out.n_audio() || out.n_midi() > 0) { - /* do not allow to add/remove channels (for now) - * The Monitor follows the master-bus and has no panner (unpan) - * but do allow processors with midi-in to be added (e.g VSTs with control that - * will remain unconnected) + /* Note: The Monitor follows the master-bus and has no panner. + * + * The general idea is to only allow plugins that retain the channel-count + * and plugins with MIDI in (e.g VSTs with control that will remain unconnected). + * Then again 5.1 in, monitor stereo is a valid use-case. + * + * and worse: we only refuse adding plugins *here*. + * + * 1) stereo-master, stereo-mon, add a stereo-plugin, OK + * 2) change master-bus, add a channel + * 2a) monitor-secion follows + * 3) monitor processors fail to re-reconfigure (stereo plugin) + * 4) re-load session, monitor-processor remains unconfigured, crash. */ - DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration not allowed.\n"); - return list > (); + DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration change.\n"); } if (boost::dynamic_pointer_cast (*p)) { // internal sends make no sense, only feedback @@ -1942,12 +1881,15 @@ Route::all_visible_processors_active (bool state) if (!(*i)->display_to_user() || boost::dynamic_pointer_cast (*i)) { continue; } - - if (state) { - (*i)->activate (); - } else { - (*i)->deactivate (); +#ifdef MIXBUS + boost::shared_ptr pi; + if (0 != (pi = boost::dynamic_pointer_cast(*i))) { + if (pi->is_channelstrip ()) { + continue; + } } +#endif + (*i)->enable (state); } _session.set_dirty (); @@ -2070,6 +2012,35 @@ Route::apply_processor_order (const ProcessorList& new_order) maybe_note_meter_position (); } +void +Route::move_instrument_down (bool postfader) +{ + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); + ProcessorList new_order; + boost::shared_ptr instrument; + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr pi = boost::dynamic_pointer_cast(*i); + if (pi && pi->plugin ()->get_info ()->is_instrument ()) { + instrument = *i; + } else if (instrument && *i == _amp) { + if (postfader) { + new_order.push_back (*i); + new_order.push_back (instrument); + } else { + new_order.push_back (instrument); + new_order.push_back (*i); + } + } else { + new_order.push_back (*i); + } + } + if (!instrument) { + return; + } + lm.release (); + reorder_processors (new_order, 0); +} + int Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err) { @@ -2341,40 +2312,30 @@ Route::get_template() 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)); } XMLNode *node = new XMLNode("Route"); ProcessorList::iterator i; - char buf[32]; - id().print (buf, sizeof (buf)); - node->add_property("id", buf); - node->add_property ("name", _name); - node->add_property("default-type", _default_type.to_string()); - node->add_property ("strict-io", _strict_io); + node->set_property ("id", id ()); + node->set_property ("name", name()); + node->set_property ("default-type", _default_type); + node->set_property ("strict-io", _strict_io); - if (_flags) { - node->add_property("flags", enum_2_string (_flags)); - } + node->add_child_nocopy (_presentation_info.get_state()); - node->add_property("active", _active?"yes":"no"); - string p; - node->add_property("denormal-protection", _denormal_protection?"yes":"no"); - node->add_property("meter-point", enum_2_string (_meter_point)); + node->set_property ("active", _active); + node->set_property ("denormal-protection", _denormal_protection); + node->set_property ("meter-point", _meter_point); - node->add_property("meter-type", enum_2_string (_meter_type)); + node->set_property ("meter-type", _meter_type); if (_route_group) { - node->add_property("route-group", _route_group->name()); + node->set_property ("route-group", _route_group->name()); } - snprintf (buf, sizeof (buf), "%d", _order_key); - node->add_property ("order-key", buf); - node->add_child_nocopy (_solo_control->get_state ()); node->add_child_nocopy (_solo_isolate_control->get_state ()); node->add_child_nocopy (_solo_safe_control->get_state ()); @@ -2390,11 +2351,6 @@ Route::state(bool full_state) node->add_child_nocopy (Automatable::get_automation_xml_state ()); } - XMLNode* remote_control_node = new XMLNode (X_("RemoteControl")); - snprintf (buf, sizeof (buf), "%d", _remote_control_id); - remote_control_node->add_property (X_("id"), buf); - node->add_child_nocopy (*remote_control_node); - if (_comment.length()) { XMLNode *cmt = node->add_child ("Comment"); cmt->add_content (_comment); @@ -2436,8 +2392,7 @@ Route::state(bool full_state) if (_custom_meter_position_noted) { boost::shared_ptr after = _processor_after_last_custom_meter.lock (); if (after) { - after->id().print (buf, sizeof (buf)); - node->add_property (X_("processor-after-last-custom-meter"), buf); + node->set_property (X_("processor-after-last-custom-meter"), after->id()); } } @@ -2460,33 +2415,23 @@ Route::set_state (const XMLNode& node, int version) XMLNodeList nlist; XMLNodeConstIterator niter; XMLNode *child; - XMLProperty const * prop; if (node.name() != "Route"){ error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg; return -1; } - if ((prop = node.property (X_("name"))) != 0) { - Route::set_name (prop->value()); + std::string route_name; + if (node.get_property (X_("name"), route_name)) { + Route::set_name (route_name); } set_id (node); _initial_io_setup = true; - if ((prop = node.property (X_("flags"))) != 0) { - _flags = Flag (string_2_enum (prop->value(), _flags)); - } else { - _flags = Flag (0); - } + Stripable::set_state (node, version); - if ((prop = node.property (X_("strict-io"))) != 0) { - _strict_io = string_is_affirmative (prop->value()); - } - - if (!can_solo()) { - _mute_master->set_solo_ignore (true); - } + node.get_property (X_("strict-io"), _strict_io); if (is_monitor()) { /* monitor bus does not get a panner, but if (re)created @@ -2508,13 +2453,14 @@ Route::set_state (const XMLNode& node, int version) child = *niter; if (child->name() == IO::state_node_name) { - if ((prop = child->property (X_("direction"))) == 0) { + std::string direction; + if (!child->get_property (X_("direction"), direction)) { continue; } - if (prop->value() == "Input") { + if (direction == "Input") { _input->set_state (*child, version); - } else if (prop->value() == "Output") { + } else if (direction == "Output") { _output->set_state (*child, version); } @@ -2526,38 +2472,20 @@ Route::set_state (const XMLNode& node, int version) } else { warning << string_compose (_("Pannable state found for route (%1) without a panner!"), name()) << endmsg; } - } else if (child->name() == Controllable::xml_node_name) { - if ((prop = child->property (X_("name"))) == 0) { - continue; - } - - if (prop->value() == _gain_control->name()) { - _gain_control->set_state (*child, version); - } else if (prop->value() == _solo_control->name()) { - _solo_control->set_state (*child, version); - } else if (prop->value() == _solo_safe_control->name()) { - _solo_safe_control->set_state (*child, version); - } else if (prop->value() == _solo_isolate_control->name()) { - _solo_isolate_control->set_state (*child, version); - } else if (prop->value() == _solo_control->name()) { - _mute_control->set_state (*child, version); - } } else if (child->name() == Slavable::xml_node_name) { Slavable::set_state (*child, version); } } - if ((prop = node.property (X_("meter-point"))) != 0) { - MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point)); + MeterPoint mp; + if (node.get_property (X_("meter-point"), mp)) { set_meter_point (mp, true); if (_meter) { _meter->set_display_to_user (_meter_point == MeterCustom); } } - if ((prop = node.property (X_("meter-type"))) != 0) { - _meter_type = MeterType (string_2_enum (prop->value (), _meter_type)); - } + node.get_property (X_("meter-type"), _meter_type); _initial_io_setup = false; @@ -2566,57 +2494,25 @@ Route::set_state (const XMLNode& node, int version) // this looks up the internal instrument in processors reset_instrument_info(); - if ((prop = node.property (X_("denormal-protection"))) != 0) { - set_denormal_protection (string_is_affirmative (prop->value())); + bool denormal_protection; + if (node.get_property (X_("denormal-protection"), denormal_protection)) { + set_denormal_protection (denormal_protection); } - if ((prop = node.property (X_("active"))) != 0) { - bool yn = string_is_affirmative (prop->value()); - set_active (yn, this); + /* convert old 3001 state */ + std::string phase_invert_str; + if (node.get_property (X_("phase-invert"), phase_invert_str)) { + _phase_control->set_phase_invert (boost::dynamic_bitset<> (phase_invert_str)); } - if ((prop = node.property (X_("order-key"))) != 0) { // New order key (no separate mixer/editor ordering) - set_order_key (atoi(prop->value())); + bool is_active; + if (node.get_property (X_("active"), is_active)) { + set_active (is_active, this); } - if ((prop = node.property (X_("order-keys"))) != 0) { // Deprecated order keys - - int32_t n; - - string::size_type colon, equal; - string remaining = prop->value(); - - while (remaining.length()) { - - if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) { - error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) - << endmsg; - } else { - if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) { - error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) - << endmsg; - } else { - string keyname = remaining.substr (0, equal); - - if ((keyname == "EditorSort") || (keyname == "editor")) { - cerr << "Setting " << name() << " order key to " << n << " using saved Editor order." << endl; - set_order_key (n); - } - } - } - - colon = remaining.find_first_of (':'); - - if (colon != string::npos) { - remaining = remaining.substr (colon+1); - } else { - break; - } - } - } - - if ((prop = node.property (X_("processor-after-last-custom-meter"))) != 0) { - PBD::ID id (prop->value ()); + std::string id_string; + if (node.get_property (X_("processor-after-last-custom-meter"), id_string)) { + PBD::ID id (id_string); Glib::Threads::RWLock::ReaderLock lm (_processor_lock); ProcessorList::const_iterator i = _processors.begin (); while (i != _processors.end() && (*i)->id() != id) { @@ -2639,20 +2535,31 @@ Route::set_state (const XMLNode& node, int version) XMLNode *cmt = *(child->children().begin()); _comment = cmt->content(); - } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) { - if (prop->value() == "solo") { - _solo_control->set_state (*child, version); - } else if (prop->value() == "mute") { - _mute_control->set_state (*child, version); + } else if (child->name() == Controllable::xml_node_name) { + std::string control_name; + if (!child->get_property (X_("name"), control_name)) { + continue; } - } else if (child->name() == X_("RemoteControl")) { - if ((prop = child->property (X_("id"))) != 0) { - int32_t x; - sscanf (prop->value().c_str(), "%d", &x); - set_remote_control_id_internal (x); + if (control_name == _solo_control->name()) { + _solo_control->set_state (*child, version); + } else if (control_name == _solo_safe_control->name()) { + _solo_safe_control->set_state (*child, version); + } else if (control_name == _solo_isolate_control->name()) { + _solo_isolate_control->set_state (*child, version); + } else if (control_name == _mute_control->name()) { + _mute_control->set_state (*child, version); + } else if (control_name == _phase_control->name()) { + _phase_control->set_state (*child, version); + } else { + Evoral::Parameter p = EventTypeMap::instance().from_symbol (control_name); + if (p.type () >= MidiCCAutomation && p.type () < MidiSystemExclusiveAutomation) { + boost::shared_ptr ac = automation_control (p, true); + if (ac) { + ac->set_state (*child, version); + } + } } - } else if (child->name() == MuteMaster::xml_node_name) { _mute_master->set_state (*child, version); @@ -2684,26 +2591,16 @@ Route::set_state_2X (const XMLNode& node, int version) return -1; } - if ((prop = node.property (X_("flags"))) != 0) { - string f = prop->value (); - boost::replace_all (f, "ControlOut", "MonitorOut"); - _flags = Flag (string_2_enum (f, _flags)); - } else { - _flags = Flag (0); - } - - if (is_master() || is_monitor() || is_auditioner()) { - _mute_master->set_solo_ignore (true); - } + Stripable::set_state (node, version); if ((prop = node.property (X_("denormal-protection"))) != 0) { - set_denormal_protection (string_is_affirmative (prop->value())); + set_denormal_protection (string_to (prop->value())); } if ((prop = node.property (X_("muted"))) != 0) { bool first = true; - bool muted = string_is_affirmative (prop->value()); + bool muted = string_to (prop->value()); if (muted) { @@ -2711,7 +2608,7 @@ Route::set_state_2X (const XMLNode& node, int version) if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) { - if (string_is_affirmative (prop->value())){ + if (string_to (prop->value())){ mute_point = mute_point + "PreFader"; first = false; } @@ -2719,7 +2616,7 @@ Route::set_state_2X (const XMLNode& node, int version) if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) { - if (string_is_affirmative (prop->value())){ + if (string_to (prop->value())){ if (!first) { mute_point = mute_point + ","; @@ -2732,7 +2629,7 @@ Route::set_state_2X (const XMLNode& node, int version) if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) { - if (string_is_affirmative (prop->value())){ + if (string_to (prop->value())){ if (!first) { mute_point = mute_point + ","; @@ -2745,7 +2642,7 @@ Route::set_state_2X (const XMLNode& node, int version) if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) { - if (string_is_affirmative (prop->value())){ + if (string_to (prop->value())){ if (!first) { mute_point = mute_point + ","; @@ -2764,46 +2661,6 @@ Route::set_state_2X (const XMLNode& node, int version) _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point)); } - /* do not carry over edit/mix groups from 2.X because (a) its hard (b) they - don't mean the same thing. - */ - - if ((prop = node.property (X_("order-keys"))) != 0) { - - int32_t n; - - string::size_type colon, equal; - string remaining = prop->value(); - - while (remaining.length()) { - - if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) { - error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) - << endmsg; - } else { - if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) { - error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining) - << endmsg; - } else { - string keyname = remaining.substr (0, equal); - - if (keyname == "EditorSort" || keyname == "editor") { - info << string_compose(_("Converting deprecated order key for %1 using Editor order %2"), name (), n) << endmsg; - set_order_key (n); - } - } - } - - colon = remaining.find_first_of (':'); - - if (colon != string::npos) { - remaining = remaining.substr (colon+1); - } else { - break; - } - } - } - /* IOs */ nlist = node.children (); @@ -2827,7 +2684,7 @@ Route::set_state_2X (const XMLNode& node, int version) set_id (*child); if ((prop = child->property (X_("active"))) != 0) { - bool yn = string_is_affirmative (prop->value()); + bool yn = string_to (prop->value()); _active = !yn; // force switch set_active (yn, this); } @@ -2893,13 +2750,6 @@ Route::set_state_2X (const XMLNode& node, int version) _mute_control->set_state (*child, version); } - } else if (child->name() == X_("RemoteControl")) { - if ((prop = child->property (X_("id"))) != 0) { - int32_t x; - sscanf (prop->value().c_str(), "%d", &x); - set_remote_control_id_internal (x); - } - } } @@ -2999,6 +2849,7 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "windows-vst" || + prop->value() == "mac-vst" || prop->value() == "lxvst" || prop->value() == "luaproc" || prop->value() == "audiounit") { @@ -3059,9 +2910,15 @@ Route::set_processor_state (const XMLNode& node) } } + ProcessorList old_list = _processors; // keep a copy { Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); + /* re-assign _processors w/o process-lock. + * if there's an IO-processor present in _processors but + * not in new_order, it will be deleted and ~IO takes + * a process lock. + */ _processors = new_order; if (must_configure) { @@ -3083,6 +2940,8 @@ Route::set_processor_state (const XMLNode& node) } } } + /* drop references w/o process-lock (I/O procs may re-take it in ~IO() */ + old_list.clear (); reset_instrument_info (); processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ @@ -3112,19 +2971,26 @@ Route::silence_unlocked (framecnt_t nframes) { /* Must be called with the processor lock held */ + const framepos_t now = _session.transport_frame (); + if (!_silent) { _output->silence (nframes); + // update owned automated controllables + automation_run (now, nframes); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr pi; if (!_active && (pi = boost::dynamic_pointer_cast (*i)) != 0) { - // skip plugins, they don't need anything when we're not active + /* evaluate automated automation controls */ + pi->automation_run (now, nframes); + /* skip plugins, they don't need anything when we're not active */ continue; } - (*i)->silence (nframes); + (*i)->silence (nframes, now); } if (nframes == _session.get_block_size()) { @@ -3219,8 +3085,7 @@ Route::add_aux_send (boost::shared_ptr route, boost::shared_ptrprocess_lock ()); - boost::shared_ptr sendpan (new Pannable (_session)); - listener.reset (new InternalSend (_session, sendpan, _mute_master, boost::dynamic_pointer_cast(shared_from_this()), route, Delivery::Aux)); + listener.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast(shared_from_this()), route, Delivery::Aux)); } add_processor (listener, before); @@ -3398,6 +3263,11 @@ Route::direct_feeds_according_to_reality (boost::shared_ptr other, bool* if (iop != 0) { boost::shared_ptr iop_out = iop->output(); + if (other.get() == this && iop_out && iop->input() && iop_out->connected_to (iop->input())) { + // TODO this needs a delaylines in the Insert to align connections (!) + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed its own return (%2)\n", iop->name(), other->name())); + continue; + } if ((iop_out && other->all_inputs().fed_by (iop_out)) || iop->feeds (other)) { DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name())); if (via_send_only) { @@ -3420,33 +3290,31 @@ Route::direct_feeds_according_to_reality (boost::shared_ptr other, bool* bool Route::direct_feeds_according_to_graph (boost::shared_ptr other, bool* via_send_only) { - return _session._current_route_graph.has (shared_from_this (), other, via_send_only); + return _session._current_route_graph.has (boost::dynamic_pointer_cast (shared_from_this ()), other, via_send_only); } bool Route::feeds_according_to_graph (boost::shared_ptr other) { - return _session._current_route_graph.feeds (shared_from_this (), other); + return _session._current_route_graph.feeds (boost::dynamic_pointer_cast (shared_from_this ()), other); } /** Called from the (non-realtime) butler thread when the transport is stopped */ void -Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool /*did_locate*/, bool can_flush_processors) +Route::non_realtime_transport_stop (framepos_t now, bool flush) { - framepos_t now = _session.transport_frame(); - { Glib::Threads::RWLock::ReaderLock lm (_processor_lock); - Automatable::transport_stopped (now); + Automatable::non_realtime_transport_stop (now, flush); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (!_have_internal_generator && (Config->get_plugins_stop_with_transport() && can_flush_processors)) { + if (!_have_internal_generator && (Config->get_plugins_stop_with_transport() && flush)) { (*i)->flush (); } - (*i)->transport_stopped (now); + (*i)->non_realtime_transport_stop (now, flush); } } @@ -3475,7 +3343,7 @@ Route::input_change_handler (IOChange change, void * /*src*/) continue; } bool sends_only; - bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only); + bool does_feed = (*i)->direct_feeds_according_to_reality (boost::dynamic_pointer_cast (shared_from_this()), &sends_only); if (does_feed && !sends_only) { if ((*i)->soloed()) { ++sbou; @@ -3552,46 +3420,56 @@ Route::output_change_handler (IOChange change, void * /*src*/) io_changed (); /* EMIT SIGNAL */ } - if (_solo_control->soloed_by_others_downstream()) { - int sbod = 0; - /* checking all all downstream routes for - * explicit of implict solo is a rather drastic measure, - * ideally the input_change_handler() of the other route - * would propagate the change to us. + if ((change.type & IOChange::ConnectionsChanged)) { + + /* do this ONLY if connections have changed. Configuration + * changes do not, by themselves alter solo upstream or + * downstream status. */ - boost::shared_ptr routes = _session.get_routes (); - if (_output->connected()) { - for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { - continue; - } - bool sends_only; - bool does_feed = direct_feeds_according_to_reality (*i, &sends_only); - if (does_feed && !sends_only) { - if ((*i)->soloed()) { - ++sbod; - break; + + if (_solo_control->soloed_by_others_downstream()) { + int sbod = 0; + /* checking all all downstream routes for + * explicit of implict solo is a rather drastic measure, + * ideally the input_change_handler() of the other route + * would propagate the change to us. + */ + boost::shared_ptr routes = _session.get_routes (); + if (_output->connected()) { + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + continue; + } + bool sends_only; + bool does_feed = direct_feeds_according_to_reality (*i, &sends_only); + if (does_feed && !sends_only) { + if ((*i)->soloed()) { + ++sbod; + break; + } } } } - } - int delta = sbod - _solo_control->soloed_by_others_downstream(); - if (delta <= 0) { - // do not allow new connections to change implicit solo (no propagation) - _solo_control->mod_solo_by_others_downstream (delta); - // Session::route_solo_changed() does not propagate indirect solo-changes - // propagate upstream to tracks - for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - if ((*i).get() == this || !can_solo()) { - continue; - } - bool sends_only; - bool does_feed = (*i)->feeds (shared_from_this(), &sends_only); - if (delta != 0 && does_feed && !sends_only) { - (*i)->solo_control()->mod_solo_by_others_downstream (delta); + + int delta = sbod - _solo_control->soloed_by_others_downstream(); + if (delta <= 0) { + // do not allow new connections to change implicit solo (no propagation) + _solo_control->mod_solo_by_others_downstream (delta); + // Session::route_solo_changed() does not propagate indirect solo-changes + // propagate upstream to tracks + boost::shared_ptr shared_this = boost::dynamic_pointer_cast (shared_from_this()); + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + if ((*i).get() == this || !can_solo()) { + continue; + } + bool sends_only; + bool does_feed = (*i)->feeds (shared_this, &sends_only); + if (delta != 0 && does_feed && !sends_only) { + (*i)->solo_control()->mod_solo_by_others_downstream (delta); + } } - } + } } } } @@ -3616,6 +3494,22 @@ Route::pans_required () const return max (n_inputs ().n_audio(), processor_max_streams.n_audio()); } +void +Route::flush_processor_buffers_locked (framecnt_t nframes) +{ + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr d = boost::dynamic_pointer_cast (*i); + if (d) { + d->flush_buffers (nframes); + } else { + boost::shared_ptr p = boost::dynamic_pointer_cast (*i); + if (p) { + p->flush_buffers (nframes); + } + } + } +} + int Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing) { @@ -3625,11 +3519,7 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, return 0; } - if (n_outputs().n_total() == 0) { - return 0; - } - - if (!_active || n_inputs() == ChanCount::ZERO) { + if (!_active) { silence_unlocked (nframes); return 0; } @@ -3654,13 +3544,15 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, fill_buffers_with_input (bufs, _input, nframes); if (_meter_point == MeterInput) { - _meter->run (bufs, start_frame, end_frame, nframes, true); + _meter->run (bufs, start_frame, end_frame, 0.0, nframes, true); } _amp->apply_gain_automation (false); _trim->apply_gain_automation (false); passthru (bufs, start_frame, end_frame, nframes, 0); + flush_processor_buffers_locked (nframes); + return 0; } @@ -3672,11 +3564,7 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in return 0; } - if (n_outputs().n_total() == 0) { - return 0; - } - - if (!_active || n_inputs().n_total() == 0) { + if (!_active) { silence_unlocked (nframes); return 0; } @@ -3694,11 +3582,13 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in fill_buffers_with_input (bufs, _input, nframes); if (_meter_point == MeterInput) { - _meter->run (bufs, start_frame, end_frame, nframes, true); + _meter->run (bufs, start_frame, end_frame, 1.0, nframes, true); } passthru (bufs, start_frame, end_frame, nframes, declick); + flush_processor_buffers_locked (nframes); + return 0; } @@ -3706,16 +3596,13 @@ int Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, bool& /* need_butler */) { silence (nframes); + flush_processor_buffers_locked (nframes); return 0; } void Route::flush_processors () { - /* XXX shouldn't really try to take this lock, since - this is called from the RT audio thread. - */ - Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { @@ -3929,7 +3816,9 @@ Route::add_export_point() Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lw (_processor_lock); - _capturing_processor.reset (new CapturingProcessor (_session)); + // this aligns all tracks; but not tracks + busses + assert (_session.worst_track_latency () >= _initial_delay); + _capturing_processor.reset (new CapturingProcessor (_session, _session.worst_track_latency () - _initial_delay)); _capturing_processor->activate (); configure_processors_unlocked (0, &lw); @@ -4126,12 +4015,22 @@ Route::set_plugin_state_dir (boost::weak_ptr p, const std::string& d) } int -Route::save_as_template (const string& path, const string& name) +Route::save_as_template (const string& path, const string& name, const string& description) { std::string state_dir = path.substr (0, path.find_last_of ('.')); // strip template_suffix PBD::Unwinder uw (_session._template_state_dir, state_dir); XMLNode& node (state (false)); + node.set_property (X_("name"), name); + + node.remove_nodes (X_("description")); + if (!description.empty()) { + XMLNode* desc = new XMLNode(X_("description")); + XMLNode* desc_cont = new XMLNode(X_("content"), description); + desc->add_child_nocopy (*desc_cont); + + node.add_child_nocopy (*desc); + } XMLTree tree; @@ -4147,6 +4046,10 @@ Route::save_as_template (const string& path, const string& name) bool Route::set_name (const string& str) { + if (str.empty ()) { + return false; + } + if (str == name()) { return true; } @@ -4183,7 +4086,7 @@ Route::set_name (const string& str) void Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playlist) { - node.add_property (X_("name"), name); + node.set_property (X_("name"), name); XMLNodeList children = node.children(); for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) { @@ -4194,17 +4097,17 @@ Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playli } else if ((*i)->name() == X_("Processor")) { - XMLProperty const * role = (*i)->property (X_("role")); - if (role && role->value() == X_("Main")) { - (*i)->add_property (X_("name"), name); + std::string str; + if ((*i)->get_property (X_("role"), str) && str == X_("Main")) { + (*i)->set_property (X_("name"), name); } } else if ((*i)->name() == X_("Diskstream")) { if (rename_playlist) { - (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str()); + (*i)->set_property (X_("playlist"), name + ".1"); } - (*i)->add_property (X_("name"), name); + (*i)->set_property (X_("name"), name); } } @@ -4259,6 +4162,7 @@ Route::set_active (bool yn, void* src) _active = yn; _input->set_active (yn); _output->set_active (yn); + flush_processors (); active_changed (); // EMIT SIGNAL _session.set_dirty (); } @@ -4707,10 +4611,17 @@ Route::setup_invisible_processors () new_processors.insert (amp, _monitor_control); } + /* TRIM CONTROL */ + + if (_trim && _trim->active()) { + assert (!_trim->display_to_user ()); + new_processors.push_front (_trim); + } + /* INTERNAL RETURN */ - /* doing this here means that any monitor control will come just after - the return. + /* doing this here means that any monitor control will come after + the return and trim. */ if (_intreturn) { @@ -4718,10 +4629,6 @@ Route::setup_invisible_processors () new_processors.push_front (_intreturn); } - if (_trim && _trim->active()) { - assert (!_trim->display_to_user ()); - new_processors.push_front (_trim); - } /* EXPORT PROCESSOR */ if (_capturing_processor) { @@ -4732,8 +4639,8 @@ Route::setup_invisible_processors () _processors = new_processors; for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (!(*i)->display_to_user () && !(*i)->active () && (*i) != _monitor_send) { - (*i)->activate (); + if (!(*i)->display_to_user () && !(*i)->enabled () && (*i) != _monitor_send) { + (*i)->enable (true); } } @@ -4874,8 +4781,10 @@ Route::the_instrument_unlocked () const void Route::non_realtime_locate (framepos_t pos) { + Automatable::non_realtime_locate (pos); + if (_pannable) { - _pannable->transport_located (pos); + _pannable->non_realtime_locate (pos); } if (_delayline.get()) { @@ -4887,7 +4796,7 @@ Route::non_realtime_locate (framepos_t pos) Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->transport_located (pos); + (*i)->non_realtime_locate (pos); } } _roll_delay = _initial_delay; @@ -4987,11 +4896,12 @@ boost::shared_ptr Route::pan_azimuth_control() const { #ifdef MIXBUS +# undef MIXBUS_PORTS_H +# include "../../gtk2_ardour/mixbus_ports.h" boost::shared_ptr plug = ch_post(); if (!plug) { return boost::shared_ptr(); } - const uint32_t port_channel_post_pan = 2; // gtk2_ardour/mixbus_ports.h return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_pan))); #else if (!_pannable || !panner()) { @@ -5019,6 +4929,12 @@ Route::pan_elevation_control() const boost::shared_ptr Route::pan_width_control() const { +#ifdef MIXBUS + if (mixbus() && _ch_pre) { + //mono blend + return boost::dynamic_pointer_cast(_ch_pre->control(Evoral::Parameter(PluginAutomation, 0, 5))); + } +#endif if (Profile->get_mixbus() || !_pannable || !panner()) { return boost::shared_ptr(); } @@ -5066,7 +4982,15 @@ uint32_t Route::eq_band_cnt () const { if (Profile->get_mixbus()) { +#ifdef MIXBUS32C + if (is_master() || mixbus()) { + return 3; + } else { + return 4; + } +#else return 3; +#endif } else { /* Ardour has no well-known EQ object */ return 0; @@ -5084,30 +5008,33 @@ Route::eq_gain_controllable (uint32_t band) const } uint32_t port_number; - switch (band) { - case 0: - if (is_master() || mixbus()) { - port_number = 4; - } else { - port_number = 8; + if (is_master() || mixbus()) { + switch (band) { + case 0: port_number = 4; break; + case 1: port_number = 3; break; + case 2: port_number = 2; break; + default: + return boost::shared_ptr(); } - break; - case 1: - if (is_master() || mixbus()) { - port_number = 3; - } else { - port_number = 6; + } else { +#ifdef MIXBUS32C + switch (band) { + case 0: port_number = 14; break; + case 1: port_number = 12; break; + case 2: port_number = 10; break; + case 3: port_number = 8; break; + default: + return boost::shared_ptr(); } - break; - case 2: - if (is_master() || mixbus()) { - port_number = 2; - } else { - port_number = 4; +#else + switch (band) { + case 0: port_number = 8; break; + case 1: port_number = 6; break; + case 2: port_number = 4; break; + default: + return boost::shared_ptr(); } - break; - default: - return boost::shared_ptr(); +#endif } return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number))); @@ -5119,7 +5046,6 @@ boost::shared_ptr Route::eq_freq_controllable (uint32_t band) const { #ifdef MIXBUS - if (mixbus() || is_master()) { /* no frequency controls for mixbusses or master */ return boost::shared_ptr(); @@ -5132,19 +5058,24 @@ Route::eq_freq_controllable (uint32_t band) const } uint32_t port_number; +#ifdef MIXBUS32C switch (band) { - case 0: - port_number = 7; - break; - case 1: - port_number = 5; - break; - case 2: - port_number = 3; - break; - default: - return boost::shared_ptr(); + case 0: port_number = 13; break; // lo + case 1: port_number = 11; break; // lo mid + case 2: port_number = 9; break; // hi mid + case 3: port_number = 7; break; // hi + default: + return boost::shared_ptr(); } +#else + switch (band) { + case 0: port_number = 7; break; + case 1: port_number = 5; break; + case 2: port_number = 3; break; + default: + return boost::shared_ptr(); + } +#endif return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number))); #else @@ -5161,6 +5092,22 @@ Route::eq_q_controllable (uint32_t band) const boost::shared_ptr Route::eq_shape_controllable (uint32_t band) const { +#ifdef MIXBUS32C + boost::shared_ptr eq = ch_eq(); + if (is_master() || mixbus() || !eq) { + return boost::shared_ptr(); + } + switch (band) { + case 0: + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 4))); // lo bell + break; + case 3: + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 3))); // hi bell + break; + default: + break; + } +#endif return boost::shared_ptr(); } @@ -5181,12 +5128,46 @@ Route::eq_enable_controllable () const } boost::shared_ptr -Route::eq_hpf_controllable () const +Route::filter_freq_controllable (bool hpf) const { #ifdef MIXBUS boost::shared_ptr eq = ch_eq(); - if (!eq) { + if (is_master() || mixbus() || !eq) { + return boost::shared_ptr(); + } + if (hpf) { +#ifdef MIXBUS32C + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 5))); // HPF freq +#else + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2))); +#endif + } else { +#ifdef MIXBUS32C + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6))); // LPF freq +#else + return boost::shared_ptr(); +#endif + } + +#else + return boost::shared_ptr(); +#endif +} + +boost::shared_ptr +Route::filter_slope_controllable (bool) const +{ + return boost::shared_ptr(); +} + +boost::shared_ptr +Route::filter_enable_controllable (bool) const +{ +#ifdef MIXBUS32C + boost::shared_ptr eq = ch_eq(); + + if (is_master() || mixbus() || !eq) { return boost::shared_ptr(); } @@ -5199,20 +5180,30 @@ Route::eq_hpf_controllable () const string Route::eq_band_name (uint32_t band) const { +#ifdef MIXBUS32C + if (is_master() || mixbus()) { +#endif if (Profile->get_mixbus()) { switch (band) { - case 0: - return _("lo"); - case 1: - return _("mid"); - case 2: - return _("hi"); - default: - return string(); + case 0: return _("lo"); + case 1: return _("mid"); + case 2: return _("hi"); + default: return string(); } } else { return string (); } +#ifdef MIXBUS32C + } else { + switch (band) { + case 0: return _("lo"); + case 1: return _("lo mid"); + case 2: return _("hi mid"); + case 3: return _("hi"); + default: return string(); + } + } +#endif } boost::shared_ptr @@ -5291,19 +5282,23 @@ Route::comp_makeup_controllable () const return boost::shared_ptr(); #endif } -boost::shared_ptr +boost::shared_ptr Route::comp_redux_controllable () const { #ifdef MIXBUS boost::shared_ptr comp = ch_comp(); if (!comp) { - return boost::shared_ptr(); + return boost::shared_ptr(); + } + if (is_master()) { + return comp->control_output (2); + } else { + return comp->control_output (6); } - return boost::dynamic_pointer_cast (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6))); #else - return boost::shared_ptr(); + return boost::shared_ptr(); #endif } @@ -5351,71 +5346,122 @@ boost::shared_ptr Route::send_level_controllable (uint32_t n) const { #ifdef MIXBUS +# undef MIXBUS_PORTS_H +# include "../../gtk2_ardour/mixbus_ports.h" boost::shared_ptr plug = ch_post(); - if (!plug) { - return boost::shared_ptr(); - } + if (plug && !mixbus()) { + uint32_t port_id = 0; + switch (n) { + case 0: port_id = port_channel_post_aux1_level; break; + case 1: port_id = port_channel_post_aux2_level; break; + case 2: port_id = port_channel_post_aux3_level; break; + case 3: port_id = port_channel_post_aux4_level; break; + case 4: port_id = port_channel_post_aux5_level; break; + case 5: port_id = port_channel_post_aux6_level; break; + case 6: port_id = port_channel_post_aux7_level; break; + case 7: port_id = port_channel_post_aux8_level; break; +# ifdef MIXBUS32C + case 8: port_id = port_channel_post_aux9_level; break; + case 9: port_id = port_channel_post_aux10_level; break; + case 10: port_id = port_channel_post_aux11_level; break; + case 11: port_id = port_channel_post_aux12_level; break; +# endif + default: + break; + } - if (n >= 8) { - /* no such bus */ - return boost::shared_ptr(); + if (port_id > 0) { + return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id))); + } +# ifdef MIXBUS32C + assert (n > 11); + n -= 12; +# else + assert (n > 7); + n -= 8; +# endif } - - const uint32_t port_id = port_channel_post_aux1_level + (2*n); // gtk2_ardour/mixbus_ports.h - return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id))); -#else +#endif boost::shared_ptr s = boost::dynamic_pointer_cast(nth_send (n)); if (!s) { return boost::shared_ptr(); } return s->gain_control (); -#endif } boost::shared_ptr Route::send_enable_controllable (uint32_t n) const { #ifdef MIXBUS +# undef MIXBUS_PORTS_H +# include "../../gtk2_ardour/mixbus_ports.h" boost::shared_ptr plug = ch_post(); - if (!plug) { - return boost::shared_ptr(); - } + if (plug && !mixbus()) { + uint32_t port_id = 0; + switch (n) { + case 0: port_id = port_channel_post_aux1_asgn; break; + case 1: port_id = port_channel_post_aux2_asgn; break; + case 2: port_id = port_channel_post_aux3_asgn; break; + case 3: port_id = port_channel_post_aux4_asgn; break; + case 4: port_id = port_channel_post_aux5_asgn; break; + case 5: port_id = port_channel_post_aux6_asgn; break; + case 6: port_id = port_channel_post_aux7_asgn; break; + case 7: port_id = port_channel_post_aux8_asgn; break; +# ifdef MIXBUS32C + case 8: port_id = port_channel_post_aux9_asgn; break; + case 9: port_id = port_channel_post_aux10_asgn; break; + case 10: port_id = port_channel_post_aux11_asgn; break; + case 11: port_id = port_channel_post_aux12_asgn; break; +# endif + default: + break; + } - if (n >= 8) { - /* no such bus */ - return boost::shared_ptr(); + if (port_id > 0) { + return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id))); + } +# ifdef MIXBUS32C + assert (n > 11); + n -= 12; +# else + assert (n > 7); + n -= 8; +# endif } - - const uint32_t port_id = port_channel_post_aux1_asgn + (2*n); // gtk2_ardour/mixbus_ports.h - return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id))); -#else +#endif /* although Ardour sends have enable/disable as part of the Processor - API, it is not exposed as a controllable. - - XXX: we should fix this. - */ + * API, it is not exposed as a controllable. + * + * XXX: we should fix this (make it click-free, automatable enable-control) + */ return boost::shared_ptr(); -#endif } string Route::send_name (uint32_t n) const { #ifdef MIXBUS - if (n >= 8) { - return string(); - } - boost::shared_ptr r = _session.get_mixbus (n); - assert (r); - return r->name(); + boost::shared_ptr plug = ch_post(); + if (plug && !mixbus()) { +# ifdef MIXBUS32C + if (n < 12) { + return _session.get_mixbus (n)->name(); + } + n -= 12; #else + if (n < 8) { + return _session.get_mixbus (n)->name(); + } + n -= 8; +# endif + } +#endif boost::shared_ptr p = nth_send (n); if (p) { return p->name(); } else { return string(); } -#endif } boost::shared_ptr @@ -5426,12 +5472,24 @@ Route::master_send_enable_controllable () const if (!plug) { return boost::shared_ptr(); } +# undef MIXBUS_PORTS_H +# include "../../gtk2_ardour/mixbus_ports.h" return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_mstr_assign))); #else return boost::shared_ptr(); #endif } +bool +Route::slaved () const +{ + if (!_gain_control) { + return false; + } + /* just test one particular control, not all of them */ + return _gain_control->slaved (); +} + bool Route::slaved_to (boost::shared_ptr vca) const { @@ -5451,17 +5509,41 @@ Route::muted_by_others_soloing () const return false; } - return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated(); + return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated(); } void Route::clear_all_solo_state () { - double v = _solo_safe_control->get_value (); - _solo_control->clear_all_solo_state (); +} - if (v != 0.0) { - _solo_safe_control->set_value (v, Controllable::NoGroup); +boost::shared_ptr +Route::automation_control_recurse (PBD::ID const & id) const +{ + boost::shared_ptr ac = Automatable::automation_control (id); + + if (ac) { + return ac; } + + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); + + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((ac = (*i)->automation_control (id))) { + return ac; + } + } + + return boost::shared_ptr (); +} + +SlavableControlList +Route::slavables () const +{ + SlavableControlList rv; + rv.push_back (_gain_control); + rv.push_back (_mute_control); + rv.push_back (_solo_control); + return rv; }