align stem-export (raw track outputs (with and w/p processing)
[ardour.git] / libs / ardour / route.cc
index a449540c6fbf82e64b5bc1d0e910c73f0f58fb54..fa564968edc3df5dc93c52ef146152cacea2aba2 100644 (file)
@@ -80,14 +80,12 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-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)
-       : 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)
@@ -98,7 +96,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 +106,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)
@@ -162,7 +156,7 @@ Route::init ()
 
        /* panning */
 
-       if (!(_flags & Route::MonitorOut)) {
+       if (!(_presentation_info.flags() & PresentationInfo::MonitorOut)) {
                _pannable.reset (new Pannable (_session));
        }
 
@@ -270,120 +264,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 +317,8 @@ Route::process_output_buffers (BufferSet& bufs,
                return;
        }
 
+       _mute_control->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 +415,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 +445,13 @@ Route::process_output_buffers (BufferSet& bufs,
                if (boost::dynamic_pointer_cast<Send>(*i) != 0) {
                        boost::dynamic_pointer_cast<Send>(*i)->set_delay_in(_signal_latency - latency);
                }
+               if (boost::dynamic_pointer_cast<PluginInsert>(*i) != 0) {
+                       const framecnt_t longest_session_latency = _initial_delay + _signal_latency;
+                       boost::dynamic_pointer_cast<PluginInsert>(*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 +479,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 +502,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 +510,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<PeakMeter>(*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 ();
                }
@@ -1130,6 +1019,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                        }
 
                        if ((*i)->active()) {
+                               // why?  emit  ActiveChanged() ??
                                (*i)->activate ();
                        }
 
@@ -1188,7 +1078,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 +1092,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 +1111,7 @@ Route::disable_plugins (Placement p)
 
        for (ProcessorList::iterator i = start; i != end; ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
-                       (*i)->deactivate ();
+                       (*i)->enable (false);
                }
        }
 
@@ -1237,7 +1127,7 @@ Route::disable_plugins ()
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
-                       (*i)->deactivate ();
+                       (*i)->enable (false);
                }
        }
 
@@ -1261,8 +1151,8 @@ Route::ab_plugins (bool forward)
                                continue;
                        }
 
-                       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);
@@ -1279,11 +1169,7 @@ Route::ab_plugins (bool forward)
                                continue;
                        }
 
-                       if ((*i)->get_next_ab_is_active()) {
-                               (*i)->activate ();
-                       } else {
-                               (*i)->deactivate ();
-                       }
+                       (*i)->enable ((*i)->get_next_ab_is_active ());
                }
        }
 
@@ -1524,7 +1410,7 @@ Route::replace_processor (boost::shared_ptr<Processor> old, boost::shared_ptr<Pr
 
                ProcessorList::iterator i;
                bool replaced = false;
-               bool enable = old->active ();
+               bool enable = old->enabled ();
 
                for (i = _processors.begin(); i != _processors.end(); ) {
                        if (*i == old) {
@@ -1568,7 +1454,7 @@ Route::replace_processor (boost::shared_ptr<Processor> old, boost::shared_ptr<Pr
                }
 
                if (enable) {
-                       sub->activate ();
+                       sub->enable (true);
                }
 
                sub->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
@@ -1737,8 +1623,8 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
 
                        if (boost::dynamic_pointer_cast<Delivery> (*p)
                                        && boost::dynamic_pointer_cast<Delivery> (*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 +1636,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 ());
@@ -1942,12 +1829,7 @@ Route::all_visible_processors_active (bool state)
                if (!(*i)->display_to_user() || boost::dynamic_pointer_cast<Amp> (*i)) {
                        continue;
                }
-
-               if (state) {
-                       (*i)->activate ();
-               } else {
-                       (*i)->deactivate ();
-               }
+               (*i)->enable (state);
        }
 
        _session.set_dirty ();
@@ -2357,9 +2239,7 @@ Route::state(bool full_state)
        node->add_property("default-type", _default_type.to_string());
        node->add_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;
@@ -2372,9 +2252,6 @@ Route::state(bool full_state)
                node->add_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 +2267,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);
@@ -2474,11 +2346,7 @@ Route::set_state (const XMLNode& node, int version)
        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());
@@ -2575,46 +2443,6 @@ Route::set_state (const XMLNode& node, int version)
                set_active (yn, this);
        }
 
-       if ((prop = node.property (X_("order-key"))) != 0) { // New order key (no separate mixer/editor ordering)
-               set_order_key (atoi(prop->value()));
-       }
-
-       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 ());
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
@@ -2646,13 +2474,6 @@ Route::set_state (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);
-                       }
-
                } else if (child->name() == MuteMaster::xml_node_name) {
                        _mute_master->set_state (*child, version);
 
@@ -2684,13 +2505,7 @@ 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);
-       }
+       Stripable::set_state (node, version);
 
        if (is_master() || is_monitor() || is_auditioner()) {
                _mute_master->set_solo_ignore (true);
@@ -2764,46 +2579,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 ();
@@ -2893,13 +2668,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);
-                       }
-
                }
        }
 
@@ -3060,9 +2828,14 @@ Route::set_processor_state (const XMLNode& node)
        }
 
        {
-               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;
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
 
                if (must_configure) {
                        configure_processors_unlocked (0, &lm);
@@ -3112,6 +2885,8 @@ 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);
@@ -3124,7 +2899,7 @@ Route::silence_unlocked (framecnt_t nframes)
                                continue;
                        }
 
-                       (*i)->silence (nframes);
+                       (*i)->silence (nframes, now);
                }
 
                if (nframes == _session.get_block_size()) {
@@ -3654,13 +3429,19 @@ 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);
 
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
+               if (d) {
+                       d->flush_buffers (nframes);
+               }
+       }
        return 0;
 }
 
@@ -3694,11 +3475,17 @@ 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);
 
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
+               if (d) {
+                       d->flush_buffers (nframes);
+               }
+       }
        return 0;
 }
 
@@ -3929,7 +3716,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);
@@ -4732,8 +4521,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);
                }
        }
 
@@ -5432,6 +5221,16 @@ Route::master_send_enable_controllable () const
 #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> vca) const
 {