X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=6a7ddde48b9e6324798515d486a865cc10260cb7;hb=facd8b2d90c74195cd4ac5c1ef7f877b78cefec9;hp=a897f5a813c46af377e3138c618c4b8c002d1042;hpb=0a87941c706a03b43ccebc229bf1df3a7a362b46;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index a897f5a813..6a7ddde48b 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -65,7 +65,6 @@ #include "ardour/session.h" #include "ardour/timestamps.h" #include "ardour/utils.h" -#include "ardour/graph.h" #include "ardour/unknown_processor.h" #include "ardour/capturing_processor.h" @@ -82,7 +81,7 @@ PBD::Signal0 Route::RemoteControlIDChange; Route::Route (Session& sess, string name, Flag flg, DataType default_type) : SessionObject (sess, name) , Automatable (sess) - , GraphNode( sess.route_graph ) + , GraphNode (sess._process_graph) , _active (true) , _signal_latency (0) , _initial_delay (0) @@ -104,9 +103,18 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _default_type (default_type) , _remote_control_id (0) , _in_configure_processors (false) + , _custom_meter_position_noted (false) + , _last_custom_meter_was_at_end (false) { processor_max_streams.reset(); order_keys[N_("signal")] = order_key_cnt++; + + if (is_master()) { + set_remote_control_id (MasterBusRemoteControlID); + } else if (is_monitor()) { + set_remote_control_id (MonitorBusRemoteControlID); + } + } int @@ -126,11 +134,7 @@ Route::init () /* panning */ if (!(_flags & Route::MonitorOut)) { - Pannable* p = new Pannable (_session); -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_shared_ptr_mark_interesting (p, "Pannable"); -#endif - _pannable.reset (p); + _pannable.reset (new Pannable (_session)); } /* input and output objects */ @@ -210,6 +214,26 @@ Route::~Route () void Route::set_remote_control_id (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; + } + + /* 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 (); @@ -401,12 +425,9 @@ Route::maybe_declick (BufferSet&, framecnt_t, int) void Route::process_output_buffers (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, - bool /*with_processors*/, int declick, - bool gain_automation_ok) + int declick, bool gain_automation_ok) { - bool monitor = should_monitor (); - - bufs.is_silent (false); + bufs.set_is_silent (false); /* figure out if we're going to use gain automation */ if (gain_automation_ok) { @@ -415,8 +436,11 @@ Route::process_output_buffers (BufferSet& bufs, _amp->apply_gain_automation (false); } - /* tell main outs what to do about monitoring */ - _main_outs->no_outs_cuz_we_no_monitor (!monitor); + /* Tell main outs what to do about monitoring. We do this so that + on a transition between monitoring states we get a de-clicking gain + change in the _main_outs delivery. + */ + _main_outs->no_outs_cuz_we_no_monitor (monitoring_state () == MonitoringSilence); /* ------------------------------------------------------------------------------------------- @@ -482,12 +506,20 @@ Route::process_output_buffers (BufferSet& bufs, and go .... ----------------------------------------------------------------------------------------- */ + /* set this to be true if the meter will already have been ::run() earlier */ + bool const meter_already_run = metering_state() == MeteringInput; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { break; } + if (boost::dynamic_pointer_cast (*i) && meter_already_run) { + /* don't ::run() the meter, otherwise it will have its previous peak corrupted */ + continue; + } + #ifndef NDEBUG /* if it has any inputs, make sure they match */ if ((*i)->input_streams() != ChanCount::ZERO) { @@ -499,6 +531,7 @@ Route::process_output_buffers (BufferSet& bufs, } } #endif + /* should we NOT run plugins here if the route is inactive? do we catch route != active somewhere higher? */ @@ -551,16 +584,17 @@ Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes } write_out_of_band_data (bufs, start_frame, end_frame, nframes); - process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true); + process_output_buffers (bufs, start_frame, end_frame, nframes, declick, true); } void Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick) { BufferSet& bufs (_session.get_silent_buffers (n_process_buffers())); + bufs.set_count (_input->n_ports()); write_out_of_band_data (bufs, start_frame, end_frame, nframes); - process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, false); + process_output_buffers (bufs, start_frame, end_frame, nframes, declick, false); } void @@ -748,7 +782,7 @@ Route::set_solo_isolated (bool yn, void *src) } bool sends_only; - bool does_feed = direct_feeds (*i, &sends_only); // we will recurse anyway, so don't use ::feeds() + bool does_feed = direct_feeds_according_to_graph (*i, &sends_only); // we will recurse anyway, so don't use ::feeds() if (does_feed && !sends_only) { (*i)->set_solo_isolated (yn, (*i)->route_group()); @@ -808,6 +842,11 @@ Route::set_mute (bool yn, void *src) if (muted() != yn) { _mute_master->set_muted_by_self (yn); + /* allow any derived classes to respond to the mute change + before anybody else knows about it. + */ + act_on_mute (); + /* tell everyone else */ mute_changed (src); /* EMIT SIGNAL */ _mute_control->Changed (); /* EMIT SIGNAL */ } @@ -1318,7 +1357,7 @@ Route::clear_processors (Placement p) } int -Route::remove_processor (boost::shared_ptr processor, ProcessorStreams* err) +Route::remove_processor (boost::shared_ptr processor, ProcessorStreams* err, bool need_process_lock) { /* these can never be removed */ @@ -1376,11 +1415,18 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream if (!removed) { /* what? */ return 1; - } + } - { + if (need_process_lock) { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + if (configure_processors_unlocked (err)) { + pstate.restore (); + /* we know this will work, because it worked before :) */ + configure_processors_unlocked (0); + return -1; + } + } else { if (configure_processors_unlocked (err)) { pstate.restore (); /* we know this will work, because it worked before :) */ @@ -1627,34 +1673,11 @@ Route::configure_processors_unlocked (ProcessorStreams* err) return 0; } -void -Route::all_processors_flip () -{ - Glib::RWLock::ReaderLock lm (_processor_lock); - - if (_processors.empty()) { - return; - } - - bool first_is_on = _processors.front()->active(); - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (first_is_on) { - (*i)->deactivate (); - } else { - (*i)->activate (); - } - } - - _session.set_dirty (); -} - -/** Set all processors with a given placement to a given active state. - * @param p Placement of processors to change. - * @param state New active state for those processors. +/** Set all visible processors to a given active state (except Fader, whose state is not changed) + * @param state New active state for those processors. */ void -Route::all_processors_active (Placement p, bool state) +Route::all_visible_processors_active (bool state) { Glib::RWLock::ReaderLock lm (_processor_lock); @@ -1662,10 +1685,11 @@ Route::all_processors_active (Placement p, bool state) return; } - ProcessorList::iterator start, end; - placement_range(p, start, end); - - for (ProcessorList::iterator i = start; i != end; ++i) { + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if (!(*i)->display_to_user() || boost::dynamic_pointer_cast (*i)) { + continue; + } + if (state) { (*i)->activate (); } else { @@ -1676,31 +1700,6 @@ Route::all_processors_active (Placement p, bool state) _session.set_dirty (); } -bool -Route::processor_is_prefader (boost::shared_ptr p) -{ - bool pre_fader = true; - Glib::RWLock::ReaderLock lm (_processor_lock); - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - - /* semantic note: if p == amp, we want to return true, so test - for equality before checking if this is the amp - */ - - if ((*i) == p) { - break; - } - - if ((*i) == _amp) { - pre_fader = false; - break; - } - } - - return pre_fader; -} - int Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err) { @@ -1769,6 +1768,9 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end()); + /* If the meter is in a custom position, find it and make a rough note of its position */ + maybe_note_meter_position (); + { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); @@ -1874,24 +1876,28 @@ Route::state(bool full_state) node->add_child_nocopy((*i)->state (full_state)); } - if (_extra_xml){ + if (_extra_xml) { node->add_child_copy (*_extra_xml); } + 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->add_property (X_("last-custom-meter-was-at-end"), _last_custom_meter_was_at_end ? "yes" : "no"); + } + return *node; } int Route::set_state (const XMLNode& node, int version) -{ - return _set_state (node, version); -} - -int -Route::_set_state (const XMLNode& node, int version) { if (version < 3000) { - return _set_state_2X (node, version); + return set_state_2X (node, version); } XMLNodeList nlist; @@ -1965,6 +1971,14 @@ Route::_set_state (const XMLNode& node, int version) } } + if ((prop = node.property (X_("meter-point"))) != 0) { + MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point)); + set_meter_point (mp, true); + if (_meter) { + _meter->set_display_to_user (_meter_point == MeterCustom); + } + } + set_processor_state (processor_state); if ((prop = node.property ("self-solo")) != 0) { @@ -2003,14 +2017,6 @@ Route::_set_state (const XMLNode& node, int version) set_active (yn, this); } - if ((prop = node.property (X_("meter-point"))) != 0) { - MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point)); - set_meter_point (mp, true); - if (_meter) { - _meter->set_display_to_user (_meter_point == MeterCustom); - } - } - if ((prop = node.property (X_("order-keys"))) != 0) { int32_t n; @@ -2042,6 +2048,24 @@ Route::_set_state (const XMLNode& node, int version) } } + if ((prop = node.property (X_("processor-after-last-custom-meter"))) != 0) { + PBD::ID id (prop->value ()); + Glib::RWLock::ReaderLock lm (_processor_lock); + ProcessorList::const_iterator i = _processors.begin (); + while (i != _processors.end() && (*i)->id() != id) { + ++i; + } + + if (i != _processors.end ()) { + _processor_after_last_custom_meter = *i; + _custom_meter_position_noted = true; + } + } + + if ((prop = node.property (X_("last-custom-meter-was-at-end"))) != 0) { + _last_custom_meter_was_at_end = string_is_affirmative (prop->value ()); + } + for (niter = nlist.begin(); niter != nlist.end(); ++niter){ child = *niter; @@ -2073,7 +2097,7 @@ Route::_set_state (const XMLNode& node, int version) } int -Route::_set_state_2X (const XMLNode& node, int version) +Route::set_state_2X (const XMLNode& node, int version) { XMLNodeList nlist; XMLNodeConstIterator niter; @@ -2360,6 +2384,7 @@ Route::set_processor_state (const XMLNode& node) new_order.push_back (_amp); } else if (prop->value() == "meter") { _meter->set_state (**niter, Stateful::current_state_version); + new_order.push_back (_meter); } else if (prop->value() == "main-outs") { _main_outs->set_state (**niter, Stateful::current_state_version); } else if (prop->value() == "intreturn") { @@ -2401,7 +2426,7 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "vst" || - prop->value() == "lxvst" || + prop->value() == "lxvst" || prop->value() == "audiounit") { processor.reset (new PluginInsert(_session)); @@ -2540,6 +2565,7 @@ Route::add_send_to_internal_return (InternalSend* send) void Route::remove_send_from_internal_return (InternalSend* send) { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); Glib::RWLock::ReaderLock rm (_processor_lock); for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) { @@ -2551,11 +2577,13 @@ Route::remove_send_from_internal_return (InternalSend* send) } } -/** Add a monitor send (if we don't already have one) but don't activate it */ -int -Route::listen_via_monitor () +void +Route::enable_monitor_send () { - /* master never sends to control outs */ + /* Caller must hold process lock */ + assert (!AudioEngine::instance()->process_lock().trylock()); + + /* master never sends to monitor section via the normal mechanism */ assert (!is_master ()); /* make sure we have one */ @@ -2565,18 +2593,15 @@ Route::listen_via_monitor () } /* set it up */ - Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); configure_processors (0); - - return 0; } -/** Add an internal send to a route. +/** Add an aux send to a route. * @param route route to send to. * @param placement placement for the send. */ int -Route::listen_via (boost::shared_ptr route, Placement placement) +Route::add_aux_send (boost::shared_ptr route, Placement placement) { assert (route != _session.monitor_out ()); @@ -2595,7 +2620,14 @@ Route::listen_via (boost::shared_ptr route, Placement placement) } try { - boost::shared_ptr listener (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux)); + + boost::shared_ptr listener; + + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux)); + } + add_processor (listener, placement); } catch (failed_constructor& err) { @@ -2606,36 +2638,39 @@ Route::listen_via (boost::shared_ptr route, Placement placement) } void -Route::drop_listen (boost::shared_ptr route) +Route::remove_aux_or_listen (boost::shared_ptr route) { ProcessorStreams err; ProcessorList::iterator tmp; - Glib::RWLock::ReaderLock rl(_processor_lock); - rl.acquire (); - - again: - for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) { - - boost::shared_ptr d = boost::dynamic_pointer_cast(*x); - - if (d && d->target_route() == route) { - rl.release (); - remove_processor (*x, &err); - rl.acquire (); + { + Glib::RWLock::ReaderLock rl(_processor_lock); - /* list could have been demolished while we dropped the lock - so start over. - */ + /* have to do this early because otherwise processor reconfig + * will put _monitor_send back in the list + */ - goto again; + if (route == _session.monitor_out()) { + _monitor_send.reset (); } - } - rl.release (); + again: + for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) { + + boost::shared_ptr d = boost::dynamic_pointer_cast(*x); + + if (d && d->target_route() == route) { + rl.release (); + remove_processor (*x, &err, false); + rl.acquire (); - if (route == _session.monitor_out()) { - _monitor_send.reset (); + /* list could have been demolished while we dropped the lock + so start over. + */ + + goto again; + } + } } } @@ -2673,7 +2708,7 @@ Route::clear_fed_by () } bool -Route::sub_feeds (Fedby const & fed_by, boost::shared_ptr other, bool* via_sends_only) +Route::feeds (boost::shared_ptr other, bool* via_sends_only) { const FedBy& fed_by (other->fed_by()); @@ -2694,15 +2729,48 @@ Route::sub_feeds (Fedby const & fed_by, boost::shared_ptr other, bool* vi } bool -Route::feeds (boost::shared_ptr other, bool* via_sends_only) +Route::direct_feeds_according_to_reality (boost::shared_ptr other, bool* via_send_only) { - return sub_feeds (other->fed_by (), other, via_sends_only); + DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name)); + + if (_output->connected_to (other->input())) { + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name())); + if (via_send_only) { + *via_send_only = false; + } + + return true; + } + + + for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) { + + boost::shared_ptr iop; + + if ((iop = boost::dynamic_pointer_cast(*r)) != 0) { + if (iop->feeds (other)) { + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name())); + if (via_send_only) { + *via_send_only = true; + } + return true; + } else { + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does NOT feed %2\n", iop->name(), other->name())); + } + } else { + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tPROC %1 is not an IOP\n", (*r)->name())); + } + + } + + DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdoes NOT feed %1\n", other->name())); + return false; } bool -Route::direct_feeds (boost::shared_ptr other, bool* via_sends_only) +Route::direct_feeds_according_to_graph (boost::shared_ptr other, bool* via_send_only) { - return sub_feeds (other->direct_fed_by (), other, via_sends_only); + return _session._current_route_graph.has (shared_from_this (), other, via_send_only); } /** Called from the (non-realtime) butler thread when the transport is stopped */ @@ -2830,14 +2898,6 @@ Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /* return 0; } -void -Route::toggle_monitor_input () -{ - for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) { - i->ensure_monitor_input( ! i->monitoring_input()); - } -} - bool Route::has_external_redirects () const { @@ -2889,45 +2949,63 @@ Route::set_meter_point (MeterPoint p, bool force) return; } - _meter_point = p; - bool meter_was_visible_to_user = _meter->display_to_user (); { Glib::RWLock::WriterLock lm (_processor_lock); + maybe_note_meter_position (); + + _meter_point = p; + if (_meter_point != MeterCustom) { _meter->set_display_to_user (false); setup_invisible_processors (); - ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter); + } else { - ChanCount m_in; + _meter->set_display_to_user (true); - if (loc == _processors.begin()) { - m_in = _input->n_ports(); - } else { - ProcessorList::iterator before = loc; - --before; - m_in = (*before)->output_streams (); + /* If we have a previous position for the custom meter, try to put it there */ + if (_custom_meter_position_noted) { + boost::shared_ptr after = _processor_after_last_custom_meter.lock (); + + if (after) { + ProcessorList::iterator i = find (_processors.begin(), _processors.end(), after); + if (i != _processors.end ()) { + _processors.remove (_meter); + _processors.insert (i, _meter); + } + } else if (_last_custom_meter_was_at_end) { + _processors.remove (_meter); + _processors.push_back (_meter); + } } + } - _meter->reflect_inputs (m_in); - - /* we do not need to reconfigure the processors, because the meter - (a) is always ready to handle processor_max_streams - (b) is always an N-in/N-out processor, and thus moving - it doesn't require any changes to the other processors. - */ + /* Set up the meter for its new position */ + ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter); + + ChanCount m_in; + + if (loc == _processors.begin()) { + m_in = _input->n_ports(); } else { - - // just make it visible and let the user move it - - _meter->set_display_to_user (true); + ProcessorList::iterator before = loc; + --before; + m_in = (*before)->output_streams (); } + + _meter->reflect_inputs (m_in); + + /* we do not need to reconfigure the processors, because the meter + (a) is always ready to handle processor_max_streams + (b) is always an N-in/N-out processor, and thus moving + it doesn't require any changes to the other processors. + */ } meter_change (); /* EMIT SIGNAL */ @@ -3258,6 +3336,38 @@ Route::set_name (const string& str) return ret; } +/** Set the name of a route in an XML description. + * @param node XML node to set the name in. + * @param name New name. + */ +void +Route::set_name_in_state (XMLNode& node, string const & name) +{ + node.add_property (X_("name"), name); + + XMLNodeList children = node.children(); + for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) { + + if ((*i)->name() == X_("IO")) { + + IO::set_name_in_state (**i, name); + + } else if ((*i)->name() == X_("Processor")) { + + XMLProperty* role = (*i)->property (X_("role")); + if (role && role->value() == X_("Main")) { + (*i)->add_property (X_("name"), name); + } + + } else if ((*i)->name() == X_("Diskstream")) { + + (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str()); + (*i)->add_property (X_("name"), name); + + } + } +} + boost::shared_ptr Route::internal_send_for (boost::shared_ptr target) const { @@ -3790,21 +3900,6 @@ Route::setup_invisible_processors () } } -bool -Route::should_monitor () const -{ - switch (Config->get_monitoring_model()) { - case HardwareMonitoring: - case ExternalMonitoring: - return !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording()); - break; - default: - break; - } - - return true; -} - void Route::unpan () { @@ -3820,3 +3915,63 @@ Route::unpan () } } } + +/** If the meter point is `Custom', make a note of where the meter is. + * This is so that if the meter point is subsequently set to something else, + * and then back to custom, we can put the meter back where it was last time + * custom was enabled. + * + * Must be called with the _processor_lock held. + */ +void +Route::maybe_note_meter_position () +{ + if (_meter_point != MeterCustom) { + return; + } + + _custom_meter_position_noted = true; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if (boost::dynamic_pointer_cast (*i)) { + ProcessorList::iterator j = i; + ++j; + if (j != _processors.end ()) { + _processor_after_last_custom_meter = *j; + _last_custom_meter_was_at_end = false; + } else { + _last_custom_meter_was_at_end = true; + } + } + } +} + +boost::shared_ptr +Route::processor_by_id (PBD::ID id) const +{ + Glib::RWLock::ReaderLock lm (_processor_lock); + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->id() == id) { + return *i; + } + } + + return boost::shared_ptr (); +} + +/** @return the monitoring state, or in other words what data we are pushing + * into the route (data from the inputs, data from disk or silence) + */ +MonitorState +Route::monitoring_state () const +{ + return MonitoringInput; +} + +/** @return what we should be metering; either the data coming from the input + * IO or the data that is flowing through the route. + */ +MeterState +Route::metering_state () const +{ + return MeteringRoute; +}