X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=d5a36481e5eecc12faabe14fc10664fe8ebd84a8;hb=77f9a1e75cb55791400c09db060901ab45c57759;hp=be5ba4f80edd0978a4d942cb0d5835595b7042d2;hpb=f07ea817511902d6ef52d3aa07dc3539fcc4d11c;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index be5ba4f80e..d5a36481e5 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,36 +81,46 @@ PBD::Signal0 Route::RemoteControlIDChange; Route::Route (Session& sess, string name, Flag flg, DataType default_type) : SessionObject (sess, name) , Automatable (sess) - , GraphNode( sess.route_graph ) - , _active (true) - , _initial_delay (0) - , _roll_delay (0) + , GraphNode (sess._process_graph) + , _active (true) + , _signal_latency (0) + , _initial_delay (0) + , _roll_delay (0) , _flags (flg) - , _pending_declick (true) - , _meter_point (MeterPostFader) - , _self_solo (false) - , _soloed_by_others_upstream (0) - , _soloed_by_others_downstream (0) - , _solo_isolated (0) - , _denormal_protection (false) - , _recordable (true) - , _silent (false) - , _declickable (false) + , _pending_declick (true) + , _meter_point (MeterPostFader) + , _self_solo (false) + , _soloed_by_others_upstream (0) + , _soloed_by_others_downstream (0) + , _solo_isolated (0) + , _denormal_protection (false) + , _recordable (true) + , _silent (false) + , _declickable (false) , _mute_master (new MuteMaster (sess, name)) - , _have_internal_generator (false) - , _solo_safe (false) + , _have_internal_generator (false) + , _solo_safe (false) , _default_type (default_type) - , _remote_control_id (0) - , _in_configure_processors (false) + , _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 Route::init () { - /* add standard controls */ + /* add standard controls */ _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ())); _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ())); @@ -122,13 +131,11 @@ Route::init () add_control (_solo_control); add_control (_mute_control); - /* panning */ - - Pannable* p = new Pannable (_session); -#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS - boost_debug_shared_ptr_mark_interesting (p, "Pannable"); -#endif - _pannable.reset (p); + /* panning */ + + if (!(_flags & Route::MonitorOut)) { + _pannable.reset (new Pannable (_session)); + } /* input and output objects */ @@ -136,8 +143,6 @@ Route::init () _output.reset (new IO (_session, _name, IO::Output, _default_type)); _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2)); - _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2)); - _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1)); /* add amp processor */ @@ -161,16 +166,16 @@ Route::init () _intreturn.reset (new InternalReturn (_session)); _intreturn->activate (); - /* the thing that provides proper control over a control/monitor/listen bus - (such as per-channel cut, dim, solo, invert, etc). - */ - _monitor_control.reset (new MonitorProcessor (_session)); + /* the thing that provides proper control over a control/monitor/listen bus + (such as per-channel cut, dim, solo, invert, etc). + */ + _monitor_control.reset (new MonitorProcessor (_session)); _monitor_control->activate (); } - if (is_master() || is_monitor() || is_hidden()) { - _mute_master->set_solo_ignore (true); - } + if (is_master() || is_monitor() || is_hidden()) { + _mute_master->set_solo_ignore (true); + } /* now that we have _meter, its safe to connect to this */ @@ -182,20 +187,20 @@ Route::init () configure_processors (0); } - return 0; + return 0; } Route::~Route () { DEBUG_TRACE (DEBUG::Destruction, string_compose ("route %1 destructor\n", _name)); - /* do this early so that we don't get incoming signals as we are going through destruction + /* do this early so that we don't get incoming signals as we are going through destruction */ drop_connections (); /* don't use clear_processors here, as it depends on the session which may - be half-destroyed by now + be half-destroyed by now */ Glib::RWLock::WriterLock lm (_processor_lock); @@ -209,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 (); @@ -292,7 +317,7 @@ Route::sync_order_keys (std::string const & base) } bool changed = false; - + for (; i != order_keys.end(); ++i) { if (i->second != key) { i->second = key; @@ -382,9 +407,9 @@ Route::set_gain (gain_t val, void *src) void Route::maybe_declick (BufferSet&, framecnt_t, int) { - /* this is the "bus" implementation and they never declick. - */ - return; + /* this is the "bus" implementation and they never declick. + */ + return; } /** Process this route for one (sub) cycle (process thread) @@ -400,29 +425,29 @@ 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) { - _amp->setup_gain_automation (start_frame, end_frame, nframes); - } else { - _amp->apply_gain_automation (false); - } - - /* tell main outs what to do about monitoring */ - _main_outs->no_outs_cuz_we_no_monitor (!monitor); + if (gain_automation_ok) { + _amp->setup_gain_automation (start_frame, end_frame, nframes); + } else { + _amp->apply_gain_automation (false); + } + + /* 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); /* ------------------------------------------------------------------------------------------- GLOBAL DECLICK (for transport changes etc.) ----------------------------------------------------------------------------------------- */ - maybe_declick (bufs, nframes, declick); + maybe_declick (bufs, nframes, declick); _pending_declick = 0; /* ------------------------------------------------------------------------------------------- @@ -481,28 +506,37 @@ 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) { - if (bufs.count() != (*i)->input_streams()) { - cerr << _name << " bufs = " << bufs.count() - << " input for " << (*i)->name() << " = " << (*i)->input_streams() - << endl; - abort (); - } - } -#endif - /* should we NOT run plugins here if the route is inactive? - do we catch route != active somewhere higher? - */ - - (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back()); + /* if it has any inputs, make sure they match */ + if ((*i)->input_streams() != ChanCount::ZERO) { + if (bufs.count() != (*i)->input_streams()) { + cerr << _name << " bufs = " << bufs.count() + << " input for " << (*i)->name() << " = " << (*i)->input_streams() + << endl; + abort (); + } + } +#endif + + /* should we NOT run plugins here if the route is inactive? + do we catch route != active somewhere higher? + */ + + (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back()); bufs.set_count ((*i)->output_streams()); } } @@ -550,33 +584,39 @@ 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 Route::set_listen (bool yn, void* src) { - if (_solo_safe) { - return; - } + if (_solo_safe) { + return; + } + + if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) { + _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group)); + return; + } if (_monitor_send) { if (yn != _monitor_send->active()) { if (yn) { _monitor_send->activate (); - _mute_master->set_soloed (true); - } else { + _mute_master->set_soloed (true); + } else { _monitor_send->deactivate (); - _mute_master->set_soloed (false); + _mute_master->set_soloed (false); } listen_changed (src); /* EMIT SIGNAL */ @@ -600,7 +640,7 @@ Route::set_solo_safe (bool yn, void *src) if (_solo_safe != yn) { _solo_safe = yn; solo_safe_changed (src); - } + } } bool @@ -623,7 +663,7 @@ Route::set_solo (bool yn, void *src) if (self_soloed() != yn) { set_self_solo (yn); - set_mute_master_solo (); + set_mute_master_solo (); solo_changed (true, src); /* EMIT SIGNAL */ _solo_control->Changed (); /* EMIT SIGNAL */ } @@ -632,17 +672,17 @@ Route::set_solo (bool yn, void *src) void Route::set_self_solo (bool yn) { - _self_solo = yn; + _self_solo = yn; } void Route::mod_solo_by_others_upstream (int32_t delta) { - if (_solo_safe) { - return; - } + if (_solo_safe) { + return; + } - uint32_t old_sbu = _soloed_by_others_upstream; + uint32_t old_sbu = _soloed_by_others_upstream; if (delta < 0) { if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) { @@ -654,50 +694,51 @@ Route::mod_solo_by_others_upstream (int32_t delta) _soloed_by_others_upstream += delta; } - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n", - name(), delta, _soloed_by_others_upstream, old_sbu, - _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo())); - - /* push the inverse solo change to everything that feeds us. - - This is important for solo-within-group. When we solo 1 track out of N that - feed a bus, that track will cause mod_solo_by_upstream (+1) to be called - on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all - tracks that feed it. This will silence them if they were audible because - of a bus solo, but the newly soloed track will still be audible (because - it is self-soloed). - - but .. do this only when we are being told to solo-by-upstream (i.e delta = +1), - not in reverse. - */ - - if ((_self_solo || _soloed_by_others_downstream) && - ((old_sbu == 0 && _soloed_by_others_upstream > 0) || - (old_sbu > 0 && _soloed_by_others_upstream == 0))) { - - if (delta > 0 || !Config->get_exclusive_solo()) { - DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n"); - for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) { - boost::shared_ptr sr = i->r.lock(); - if (sr) { - sr->mod_solo_by_others_downstream (-delta); - } - } - } - } - - set_mute_master_solo (); - solo_changed (false, this); + DEBUG_TRACE (DEBUG::Solo, string_compose ( + "%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n", + name(), delta, _soloed_by_others_upstream, old_sbu, + _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo())); + + /* push the inverse solo change to everything that feeds us. + + This is important for solo-within-group. When we solo 1 track out of N that + feed a bus, that track will cause mod_solo_by_upstream (+1) to be called + on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all + tracks that feed it. This will silence them if they were audible because + of a bus solo, but the newly soloed track will still be audible (because + it is self-soloed). + + but .. do this only when we are being told to solo-by-upstream (i.e delta = +1), + not in reverse. + */ + + if ((_self_solo || _soloed_by_others_downstream) && + ((old_sbu == 0 && _soloed_by_others_upstream > 0) || + (old_sbu > 0 && _soloed_by_others_upstream == 0))) { + + if (delta > 0 || !Config->get_exclusive_solo()) { + DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n"); + for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) { + boost::shared_ptr sr = i->r.lock(); + if (sr) { + sr->mod_solo_by_others_downstream (-delta); + } + } + } + } + + set_mute_master_solo (); + solo_changed (false, this); } void Route::mod_solo_by_others_downstream (int32_t delta) { - if (_solo_safe) { - return; - } + if (_solo_safe) { + return; + } - if (delta < 0) { + if (delta < 0) { if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) { _soloed_by_others_downstream += delta; } else { @@ -707,16 +748,16 @@ Route::mod_solo_by_others_downstream (int32_t delta) _soloed_by_others_downstream += delta; } - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream)); + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream)); - set_mute_master_solo (); - solo_changed (false, this); + set_mute_master_solo (); + solo_changed (false, this); } void Route::set_mute_master_solo () { - _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream()); + _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream()); } void @@ -730,47 +771,47 @@ Route::set_solo_isolated (bool yn, void *src) _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, _route_group)); return; } - + /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */ boost::shared_ptr routes = _session.get_routes (); for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) { - continue; - } + continue; + } 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()); } } - /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ + /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ - bool changed = false; + bool changed = false; if (yn) { - if (_solo_isolated == 0) { - _mute_master->set_solo_ignore (true); - changed = true; - } + if (_solo_isolated == 0) { + _mute_master->set_solo_ignore (true); + changed = true; + } _solo_isolated++; } else { if (_solo_isolated > 0) { _solo_isolated--; - if (_solo_isolated == 0) { - _mute_master->set_solo_ignore (false); - changed = true; - } + if (_solo_isolated == 0) { + _mute_master->set_solo_ignore (false); + changed = true; + } } } - if (changed) { - solo_isolated_changed (src); - } + if (changed) { + solo_isolated_changed (src); + } } bool @@ -782,13 +823,13 @@ Route::solo_isolated () const void Route::set_mute_points (MuteMaster::MutePoint mp) { - _mute_master->set_mute_points (mp); - mute_points_changed (); /* EMIT SIGNAL */ - - if (_mute_master->muted_by_self()) { - mute_changed (this); /* EMIT SIGNAL */ + _mute_master->set_mute_points (mp); + mute_points_changed (); /* EMIT SIGNAL */ + + if (_mute_master->muted_by_self()) { + mute_changed (this); /* EMIT SIGNAL */ _mute_control->Changed (); /* EMIT SIGNAL */ - } + } } void @@ -800,7 +841,12 @@ Route::set_mute (bool yn, void *src) } if (muted() != yn) { - _mute_master->set_muted_by_self (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 */ } @@ -809,7 +855,7 @@ Route::set_mute (bool yn, void *src) bool Route::muted () const { - return _mute_master->muted_by_self(); + return _mute_master->muted_by_self(); } #if 0 @@ -819,14 +865,14 @@ dump_processors(const string& name, const list >& p cerr << name << " {" << endl; for (list >::const_iterator p = procs.begin(); p != procs.end(); ++p) { - cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl; + cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << " @ " << (*p) << endl; } cerr << "}" << endl; } #endif int -Route::add_processor (boost::shared_ptr processor, Placement placement, ProcessorStreams* err) +Route::add_processor (boost::shared_ptr processor, Placement placement, ProcessorStreams* err, bool activation_allowed) { ProcessorList::iterator loc; @@ -842,10 +888,38 @@ Route::add_processor (boost::shared_ptr processor, Placement placemen loc = find (_processors.begin(), _processors.end(), _main_outs); } - return add_processor (processor, loc, err); + return add_processor (processor, loc, err, activation_allowed); } +/** Add a processor to a route such that it ends up with a given index into the visible processors. + * @param index Index to add the processor at, or -1 to add at the end of the list. + */ + +int +Route::add_processor_by_index (boost::shared_ptr processor, int index, ProcessorStreams* err, bool activation_allowed) +{ + /* XXX this is not thread safe - we don't hold the lock across determining the iter + to add before and actually doing the insertion. dammit. + */ + + if (index == -1) { + return add_processor (processor, _processors.end(), err, activation_allowed); + } + + ProcessorList::iterator i = _processors.begin (); + int j = 0; + while (i != _processors.end() && j < index) { + if ((*i)->display_to_user()) { + ++j; + } + + ++i; + } + + return add_processor (processor, i, err, activation_allowed); +} + /** Add a processor to the route. * @param iter an iterator in _processors; the new processor will be inserted immediately before this location. */ @@ -855,9 +929,8 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite assert (processor != _meter); assert (processor != _main_outs); - DEBUG_TRACE (DEBUG::Processors, string_compose ("%1 adding processor %2\n", name(), processor->name())); - - ChanCount old_pms = processor_max_streams; + DEBUG_TRACE (DEBUG::Processors, string_compose ( + "%1 adding processor %2\n", name(), processor->name())); if (!_session.engine().connected() || !processor) { return 1; @@ -908,7 +981,7 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite if ((pi = boost::dynamic_pointer_cast(processor)) != 0) { - if (pi->natural_input_streams() == ChanCount::ZERO) { + if (pi->has_no_inputs ()) { /* generator plugin */ _have_internal_generator = true; } @@ -919,7 +992,7 @@ Route::add_processor (boost::shared_ptr processor, ProcessorList::ite processor->activate (); } - processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); + processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false)); _output->set_user_latency (0); } @@ -944,7 +1017,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) XMLNodeList const & children = node.children (); XMLNodeList::const_iterator i = children.begin (); - + while (i != children.end() && (*i)->name() != X_("Redirect")) { ++i; } @@ -961,9 +1034,10 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) if ((prop = node.property ("type")) != 0) { - if (prop->value() == "ladspa" || prop->value() == "Ladspa" || + if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "vst" || + prop->value() == "lxvst" || prop->value() == "audiounit") { processor.reset (new PluginInsert (_session)); @@ -985,9 +1059,9 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) return false; } - if (processor->set_state (node, version)) { - return false; - } + if (processor->set_state (node, version)) { + return false; + } return (add_processor (processor, placement) == 0); } @@ -1015,8 +1089,6 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr loc = _processors.end (); } - ChanCount old_pms = processor_max_streams; - if (!_session.engine().connected()) { return 1; } @@ -1041,7 +1113,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr pi->set_count (1); } - ProcessorList::iterator inserted = _processors.insert (loc, *i); + _processors.insert (loc, *i); if ((*i)->active()) { (*i)->activate (); @@ -1056,14 +1128,14 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr } } - (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); + (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false)); } - + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { - if (pi->is_generator()) { + if (pi->has_no_inputs ()) { _have_internal_generator = true; break; } @@ -1213,8 +1285,6 @@ Route::ab_plugins (bool forward) void Route::clear_processors (Placement p) { - const ChanCount old_pms = processor_max_streams; - if (!_session.engine().connected()) { return; } @@ -1287,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 */ @@ -1295,8 +1365,6 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream return 0; } - ChanCount old_pms = processor_max_streams; - if (!_session.engine().connected()) { return 1; } @@ -1306,7 +1374,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream { Glib::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); - + ProcessorList::iterator i; bool removed = false; @@ -1347,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 :) */ @@ -1366,7 +1441,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { - if (pi->is_generator()) { + if (pi->has_no_inputs ()) { _have_internal_generator = true; break; } @@ -1395,7 +1470,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* { Glib::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); - + ProcessorList::iterator i; boost::shared_ptr processor; @@ -1441,7 +1516,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - + if (configure_processors_unlocked (err)) { pstate.restore (); /* we know this will work, because it worked before :) */ @@ -1456,7 +1531,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { - if (pi->is_generator()) { + if (pi->has_no_inputs ()) { _have_internal_generator = true; break; } @@ -1481,19 +1556,19 @@ int Route::configure_processors (ProcessorStreams* err) { assert (!AudioEngine::instance()->process_lock().trylock()); - + if (!_in_configure_processors) { Glib::RWLock::WriterLock lm (_processor_lock); return configure_processors_unlocked (err); } - + return 0; } ChanCount Route::input_streams () const { - return _input->n_ports (); + return _input->n_ports (); } list > @@ -1521,7 +1596,7 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n"); break; } - + if ((*p)->can_support_io_configuration(in, out)) { DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out)); configuration.push_back(make_pair(in, out)); @@ -1539,7 +1614,7 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) } DEBUG_TRACE (DEBUG::Processors, "}\n"); - + return configuration; } @@ -1576,7 +1651,7 @@ Route::configure_processors_unlocked (ProcessorStreams* err) if (boost::dynamic_pointer_cast (*p)) { break; } - + (*p)->configure_io(c->first, c->second); processor_max_streams = ChanCount::max(processor_max_streams, c->first); processor_max_streams = ChanCount::max(processor_max_streams, c->second); @@ -1588,7 +1663,8 @@ Route::configure_processors_unlocked (ProcessorStreams* err) } /* make sure we have sufficient scratch buffers to cope with the new processor - configuration */ + configuration + */ _session.ensure_buffers (n_process_buffers ()); DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name)); @@ -1597,86 +1673,33 @@ 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); if (_processors.empty()) { return; } - ProcessorList::iterator start, end; - placement_range(p, start, end); - - bool before_amp = true; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i) == _amp) { - before_amp = false; + if (!(*i)->display_to_user() || boost::dynamic_pointer_cast (*i)) { continue; } - if (p == PreFader && before_amp) { - if (state) { - (*i)->activate (); - } else { - (*i)->deactivate (); - } + + if (state) { + (*i)->activate (); + } else { + (*i)->deactivate (); } } _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) { @@ -1689,7 +1712,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err { Glib::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); - + ProcessorList::iterator oiter; ProcessorList::const_iterator niter; ProcessorList as_it_will_be; @@ -1737,7 +1760,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err } } - /* now remove from old order - its taken care of no matter what */ + /* now remove from old order - its taken care of no matter what */ oiter = _processors.erase (oiter); } @@ -1745,9 +1768,12 @@ 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 ()); - + if (configure_processors_unlocked (err)) { pstate.restore (); return -1; @@ -1842,30 +1868,36 @@ Route::state(bool full_state) cmt->add_content (_comment); } - node->add_child_nocopy (_pannable->state (full_state)); + if (_pannable) { + node->add_child_nocopy (_pannable->state (full_state)); + } for (i = _processors.begin(); i != _processors.end(); ++i) { 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, true); -} - -int -Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) { if (version < 3000) { - return _set_state_2X (node, version); + return set_state_2X (node, version); } XMLNodeList nlist; @@ -1882,9 +1914,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) Route::set_name (prop->value()); } - if ((prop = node.property ("id")) != 0) { - _id = prop->value (); - } + set_id (node); if ((prop = node.property (X_("flags"))) != 0) { _flags = Flag (string_2_enum (prop->value(), _flags)); @@ -1892,15 +1922,25 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) _flags = Flag (0); } - if (is_master() || is_monitor() || is_hidden()) { - _mute_master->set_solo_ignore (true); - } + if (is_master() || is_monitor() || is_hidden()) { + _mute_master->set_solo_ignore (true); + } + + if (is_monitor()) { + /* monitor bus does not get a panner, but if (re)created + via XML, it will already have one by the time we + call ::set_state(). so ... remove it. + */ + unpan (); + } /* add all processors (except amp, which is always present) */ nlist = node.children(); XMLNode processor_state (X_("processor_state")); + Stateful::save_extra_xml (node); + for (niter = nlist.begin(); niter != nlist.end(); ++niter){ child = *niter; @@ -1922,9 +1962,21 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) } - if (child->name() == X_("Pannable")) { - _pannable->set_state (*child, version); - } + if (child->name() == X_("Pannable")) { + if (_pannable) { + _pannable->set_state (*child, version); + } else { + warning << string_compose (_("Pannable state found for route (%1) without a panner!"), name()) << endmsg; + } + } + } + + 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); @@ -1965,14 +2017,6 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) 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; @@ -2004,6 +2048,24 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) } } + 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; @@ -2014,10 +2076,6 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) XMLNode *cmt = *(child->children().begin()); _comment = cmt->content(); - } else if (child->name() == X_("Extra")) { - - _extra_xml = new XMLNode (*child); - } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) { if (prop->value() == "solo") { _solo_control->set_state (*child, version); @@ -2039,7 +2097,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) } int -Route::_set_state_2X (const XMLNode& node, int version) +Route::set_state_2X (const XMLNode& node, int version) { XMLNodeList nlist; XMLNodeConstIterator niter; @@ -2062,12 +2120,12 @@ Route::_set_state_2X (const XMLNode& node, int version) } else { _flags = Flag (0); } - + if ((prop = node.property (X_("phase-invert"))) != 0) { boost::dynamic_bitset<> p (_input->n_ports().n_audio ()); if (string_is_affirmative (prop->value ())) { p.set (); - } + } set_phase_invert (p); } @@ -2084,60 +2142,60 @@ Route::_set_state_2X (const XMLNode& node, int version) } if ((prop = node.property (X_("muted"))) != 0) { - + bool first = true; bool muted = string_is_affirmative (prop->value()); - + if (muted) { string mute_point; - + if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) { - + if (string_is_affirmative (prop->value())){ mute_point = mute_point + "PreFader"; first = false; } } - + if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) { - + if (string_is_affirmative (prop->value())){ - + if (!first) { mute_point = mute_point + ","; } - + mute_point = mute_point + "PostFader"; first = false; } } if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) { - + if (string_is_affirmative (prop->value())){ - + if (!first) { mute_point = mute_point + ","; } - + mute_point = mute_point + "Listen"; first = false; } } if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) { - + if (string_is_affirmative (prop->value())){ - + if (!first) { mute_point = mute_point + ","; } - + mute_point = mute_point + "Main"; } } - + _mute_master->set_mute_points (mute_point); _mute_master->set_muted_by_self (true); } @@ -2202,16 +2260,14 @@ Route::_set_state_2X (const XMLNode& node, int version) Route::set_name (prop->value ()); } - if ((prop = child->property (X_("id"))) != 0) { - _id = prop->value (); - } + set_id (*child); if ((prop = child->property (X_("active"))) != 0) { bool yn = string_is_affirmative (prop->value()); _active = !yn; // force switch set_active (yn, this); } - + if ((prop = child->property (X_("gain"))) != 0) { gain_t val; @@ -2219,17 +2275,17 @@ Route::_set_state_2X (const XMLNode& node, int version) _amp->gain_control()->set_value (val); } } - + /* Set up Panners in the IO */ XMLNodeList io_nlist = child->children (); - + XMLNodeConstIterator io_niter; XMLNode *io_child; - + for (io_niter = io_nlist.begin(); io_niter != io_nlist.end(); ++io_niter) { io_child = *io_niter; - + if (io_child->name() == X_("Panner")) { _main_outs->panner_shell()->set_state(*io_child, version); } else if (io_child->name() == X_("Automation")) { @@ -2254,6 +2310,8 @@ Route::_set_state_2X (const XMLNode& node, int version) set_processor_state_2X (redirect_nodes, version); + Stateful::save_extra_xml (node); + for (niter = nlist.begin(); niter != nlist.end(); ++niter){ child = *niter; @@ -2264,17 +2322,13 @@ Route::_set_state_2X (const XMLNode& node, int version) XMLNode *cmt = *(child->children().begin()); _comment = cmt->content(); - } else if (child->name() == X_("extra")) { - - _extra_xml = new XMLNode (*child); - } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) { if (prop->value() == X_("solo")) { _solo_control->set_state (*child, version); } else if (prop->value() == X_("mute")) { _mute_control->set_state (*child, version); - } - + } + } else if (child->name() == X_("RemoteControl")) { if ((prop = child->property (X_("id"))) != 0) { int32_t x; @@ -2282,7 +2336,7 @@ Route::_set_state_2X (const XMLNode& node, int version) set_remote_control_id (x); } - } + } } return 0; @@ -2318,74 +2372,77 @@ Route::set_processor_state (const XMLNode& node) { const XMLNodeList &nlist = node.children(); XMLNodeConstIterator niter; - ProcessorList new_order; - bool must_configure = false; + ProcessorList new_order; + bool must_configure = false; for (niter = nlist.begin(); niter != nlist.end(); ++niter) { XMLProperty* prop = (*niter)->property ("type"); if (prop->value() == "amp") { - _amp->set_state (**niter, Stateful::current_state_version); - new_order.push_back (_amp); - } else if (prop->value() == "meter") { - _meter->set_state (**niter, Stateful::current_state_version); - } else if (prop->value() == "main-outs") { - _main_outs->set_state (**niter, Stateful::current_state_version); - } else if (prop->value() == "intreturn") { - if (!_intreturn) { - _intreturn.reset (new InternalReturn (_session)); - must_configure = true; - } - _intreturn->set_state (**niter, Stateful::current_state_version); - } else if (is_monitor() && prop->value() == "monitor") { - if (!_monitor_control) { - _monitor_control.reset (new MonitorProcessor (_session)); - must_configure = true; - } - _monitor_control->set_state (**niter, Stateful::current_state_version); + _amp->set_state (**niter, Stateful::current_state_version); + 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") { + if (!_intreturn) { + _intreturn.reset (new InternalReturn (_session)); + must_configure = true; + } + _intreturn->set_state (**niter, Stateful::current_state_version); + } else if (is_monitor() && prop->value() == "monitor") { + if (!_monitor_control) { + _monitor_control.reset (new MonitorProcessor (_session)); + must_configure = true; + } + _monitor_control->set_state (**niter, Stateful::current_state_version); } else if (prop->value() == "capture") { _capturing_processor.reset (new CapturingProcessor (_session)); - } else { - ProcessorList::iterator o; + } else { + ProcessorList::iterator o; for (o = _processors.begin(); o != _processors.end(); ++o) { XMLProperty* id_prop = (*niter)->property(X_("id")); if (id_prop && (*o)->id() == id_prop->value()) { - (*o)->set_state (**niter, Stateful::current_state_version); - new_order.push_back (*o); + (*o)->set_state (**niter, Stateful::current_state_version); + new_order.push_back (*o); break; } } - // If the processor (*niter) is not on the route then create it - - if (o == _processors.end()) { - - boost::shared_ptr processor; - - if (prop->value() == "intsend") { - - processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr(), Delivery::Role (0))); - } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || - prop->value() == "lv2" || - prop->value() == "vst" || - prop->value() == "audiounit") { - - processor.reset (new PluginInsert(_session)); - - } else if (prop->value() == "port") { - - processor.reset (new PortInsert (_session, _pannable, _mute_master)); - - } else if (prop->value() == "send") { - - processor.reset (new Send (_session, _pannable, _mute_master)); - - } else { - error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; - continue; - } + // If the processor (*niter) is not on the route then create it + + if (o == _processors.end()) { + + boost::shared_ptr processor; + + if (prop->value() == "intsend") { + + processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr(), Delivery::Role (0))); + + } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || + prop->value() == "lv2" || + prop->value() == "vst" || + prop->value() == "lxvst" || + prop->value() == "audiounit") { + + processor.reset (new PluginInsert(_session)); + + } else if (prop->value() == "port") { + + processor.reset (new PortInsert (_session, _pannable, _mute_master)); + + } else if (prop->value() == "send") { + + processor.reset (new Send (_session, _pannable, _mute_master)); + + } else { + error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; + continue; + } if (processor->set_state (**niter, Stateful::current_state_version) != 0) { /* This processor could not be configured. Turn it into a UnknownProcessor */ @@ -2406,32 +2463,35 @@ Route::set_processor_state (const XMLNode& node) new_order.push_back (processor); must_configure = true; - } - } - } + } + } + } { Glib::RWLock::WriterLock lm (_processor_lock); - _processors = new_order; + _processors = new_order; - if (must_configure) { + if (must_configure) { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - configure_processors_unlocked (0); - } + configure_processors_unlocked (0); + } for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + + (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false)); + boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { - if (pi->is_generator()) { + if (pi->has_no_inputs ()) { _have_internal_generator = true; break; } } } - } + } - processors_changed (RouteProcessorChange ()); + processors_changed (RouteProcessorChange ()); set_processor_positions (); } @@ -2457,22 +2517,22 @@ void Route::silence_unlocked (framecnt_t nframes) { /* Must be called with the processor lock held */ - + if (!_silent) { _output->silence (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 continue; } - + (*i)->silence (nframes); } - + if (nframes == _session.get_block_size()) { // _silent = true; } @@ -2491,6 +2551,7 @@ Route::add_internal_return () void Route::add_send_to_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) { @@ -2505,6 +2566,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) { @@ -2516,35 +2578,34 @@ 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 */ if (!_monitor_send) { _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen)); _monitor_send->set_display_to_user (false); } - + /* 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 ()); - + { Glib::RWLock::ReaderLock rm (_processor_lock); @@ -2560,7 +2621,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) { @@ -2571,36 +2639,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; + } + } } } @@ -2615,64 +2686,64 @@ Route::set_comment (string cmt, void *src) bool Route::add_fed_by (boost::shared_ptr other, bool via_sends_only) { - FeedRecord fr (other, via_sends_only); + FeedRecord fr (other, via_sends_only); - pair result = _fed_by.insert (fr); + pair result = _fed_by.insert (fr); - if (!result.second) { + if (!result.second) { - /* already a record for "other" - make sure sends-only information is correct */ - if (!via_sends_only && result.first->sends_only) { - FeedRecord* frp = const_cast(&(*result.first)); - frp->sends_only = false; - } - } - - return result.second; + /* already a record for "other" - make sure sends-only information is correct */ + if (!via_sends_only && result.first->sends_only) { + FeedRecord* frp = const_cast(&(*result.first)); + frp->sends_only = false; + } + } + + return result.second; } void Route::clear_fed_by () { - _fed_by.clear (); + _fed_by.clear (); } bool Route::feeds (boost::shared_ptr other, bool* via_sends_only) { - const FedBy& fed_by (other->fed_by()); + const FedBy& fed_by (other->fed_by()); - for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) { - boost::shared_ptr sr = f->r.lock(); + for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) { + boost::shared_ptr sr = f->r.lock(); - if (sr && (sr.get() == this)) { + if (sr && (sr.get() == this)) { - if (via_sends_only) { - *via_sends_only = f->sends_only; - } + if (via_sends_only) { + *via_sends_only = f->sends_only; + } - return true; - } - } + return true; + } + } - return false; + return false; } bool -Route::direct_feeds (boost::shared_ptr other, bool* only_send) +Route::direct_feeds_according_to_reality (boost::shared_ptr other, bool* via_send_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 (only_send) { - *only_send = false; + if (via_send_only) { + *via_send_only = false; } return true; } - + for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) { boost::shared_ptr iop; @@ -2680,8 +2751,8 @@ Route::direct_feeds (boost::shared_ptr other, bool* only_send) 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 (only_send) { - *only_send = true; + if (via_send_only) { + *via_send_only = true; } return true; } else { @@ -2690,19 +2761,25 @@ Route::direct_feeds (boost::shared_ptr other, bool* only_send) } 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_according_to_graph (boost::shared_ptr other, bool* via_send_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 */ void Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors) { framepos_t now = _session.transport_frame(); - + { Glib::RWLock::ReaderLock lm (_processor_lock); @@ -2710,14 +2787,14 @@ Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_lo automation_snapshot (now, true); } - Automatable::transport_stopped (now); + Automatable::transport_stopped (now); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (!_have_internal_generator && (Config->get_plugins_stop_with_transport() && can_flush_processors)) { - (*i)->flush (); + (*i)->flush (); } - + (*i)->transport_stopped (now); } } @@ -2736,45 +2813,6 @@ Route::input_change_handler (IOChange change, void * /*src*/) } } -/** Called with the process lock held if change contains ConfigurationChanged */ -void -Route::output_change_handler (IOChange change, void * /*src*/) -{ - if ((change.type & IOChange::ConfigurationChanged)) { - - /* XXX resize all listeners to match _main_outs? */ - - /* Auto-connect newly-created outputs, unless we're auto-connecting to master - and we are master (as an auto-connect in this situation would cause a - feedback loop) - */ - AutoConnectOption ac = Config->get_output_auto_connect (); - if (ac == AutoConnectPhysical || (ac == AutoConnectMaster && !is_master ())) { - - ChanCount start = change.before; - - for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) { - if (change.before.get(*i) < change.after.get(*i)) { - /* the existing ChanCounts don't matter for this call as they are only - to do with matching input and output indices, and we are only changing - outputs here. - */ - ChanCount dummy; - - /* only auto-connect the newly-created outputs, not the ones that were - already there - */ - start.set (*i, start.get (*i) + 1); - - _session.auto_connect_route (this, dummy, dummy, false, ChanCount(), change.before); - } - } - } - - // configure_processors (0); - } -} - uint32_t Route::pans_required () const { @@ -2786,8 +2824,7 @@ Route::pans_required () const } int -Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, - bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/) +Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing) { Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); if (!lm.locked()) { @@ -2806,7 +2843,7 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, if (_session.transport_speed() != 0.0f) { /* we're rolling but some state is changing (e.g. our diskstream contents) so we cannot use them. Be silent till this is over. - + XXX note the absurdity of ::no_roll() being called when we ARE rolling! */ silence_unlocked (nframes); @@ -2823,43 +2860,14 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, return 0; } -framecnt_t -Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame) -{ - if (_roll_delay > nframes) { - - _roll_delay -= nframes; - silence_unlocked (nframes); - /* transport frame is not legal for caller to use */ - return 0; - - } else if (_roll_delay > 0) { - - nframes -= _roll_delay; - silence_unlocked (_roll_delay); - /* we've written _roll_delay of samples into the - output ports, so make a note of that for - future reference. - */ - - _main_outs->increment_output_offset (_roll_delay); - transport_frame += _roll_delay; - - _roll_delay = 0; - } - - return nframes; -} - int -Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, - bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */) +Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& /* need_butler */) { Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK); if (!lm.locked()) { return 0; } - + automation_snapshot (_session.transport_frame(), false); if (n_outputs().n_total() == 0) { @@ -2885,21 +2893,12 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in } int -Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, - bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */) +Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, bool& /* need_butler */) { silence (nframes); 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 { @@ -2938,7 +2937,7 @@ Route::flush_processors () Glib::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->flush (); + (*i)->flush (); } } @@ -2951,51 +2950,69 @@ 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); - ChanCount m_in; - - if (loc == _processors.begin()) { - m_in = _input->n_ports(); - } else { - 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. - */ - } else { - - // just make it visible and let the user move it - + _meter->set_display_to_user (true); + + /* 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); + } + } + } + + /* 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 { + 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 */ bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user); - + processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */ } @@ -3008,7 +3025,7 @@ Route::listen_position_changed () { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - + if (configure_processors_unlocked (0)) { pstate.restore (); configure_processors_unlocked (0); // it worked before we tried to add it ... @@ -3025,7 +3042,7 @@ boost::shared_ptr Route::add_export_point() { if (!_capturing_processor) { - + _capturing_processor.reset (new CapturingProcessor (_session)); _capturing_processor->activate (); @@ -3035,66 +3052,54 @@ Route::add_export_point() } } - + return _capturing_processor; } framecnt_t -Route::update_total_latency () +Route::update_signal_latency () { - framecnt_t old = _output->effective_latency(); - framecnt_t own_latency = _output->user_latency(); + framecnt_t l = _output->user_latency(); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((*i)->active ()) { - own_latency += (*i)->signal_latency (); + l += (*i)->signal_latency (); } } - DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal redirect latency = %2\n", _name, own_latency)); - - _output->set_port_latency (own_latency); + DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l)); - if (_output->user_latency() == 0) { - - /* this (virtual) function is used for pure Routes, - not derived classes like AudioTrack. this means - that the data processed here comes from an input - port, not prerecorded material, and therefore we - have to take into account any input latency. - */ - - own_latency += _input->signal_latency (); - } - - if (old != own_latency) { - _output->set_latency_delay (own_latency); + if (_signal_latency != l) { + _signal_latency = l; signal_latency_changed (); /* EMIT SIGNAL */ } - DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: input latency = %2 total = %3\n", _name, _input->signal_latency(), own_latency)); - - return _output->effective_latency (); + return _signal_latency; } void Route::set_user_latency (framecnt_t nframes) { _output->set_user_latency (nframes); - _session.update_latency_compensation (false, false); + _session.update_latency_compensation (); } void -Route::set_latency_delay (framecnt_t longest_session_latency) +Route::set_latency_compensation (framecnt_t longest_session_latency) { framecnt_t old = _initial_delay; - if (_output->effective_latency() < longest_session_latency) { - _initial_delay = longest_session_latency - _output->effective_latency(); + if (_signal_latency < longest_session_latency) { + _initial_delay = longest_session_latency - _signal_latency; } else { _initial_delay = 0; } + DEBUG_TRACE (DEBUG::Latency, string_compose ( + "%1: compensate for maximum latency of %2," + "given own latency of %3, using initial delay of %4\n", + name(), longest_session_latency, _signal_latency, _initial_delay)); + if (_initial_delay != old) { initial_delay_changed (); /* EMIT SIGNAL */ } @@ -3107,7 +3112,10 @@ Route::set_latency_delay (framecnt_t longest_session_latency) void Route::automation_snapshot (framepos_t now, bool force) { - _pannable->automation_snapshot (now, force); + if (_pannable) { + _pannable->automation_snapshot (now, force); + } + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->automation_snapshot (now, force); } @@ -3133,7 +3141,7 @@ Route::SoloControllable::set_value (double val) if (!r) { return; } - + rl->push_back (r); if (Config->get_solo_control_is_listen_control()) { @@ -3150,7 +3158,7 @@ Route::SoloControllable::get_value () const if (!r) { return 0; } - + if (Config->get_solo_control_is_listen_control()) { return r->listening_via_monitor() ? 1.0f : 0.0f; } else { @@ -3178,7 +3186,7 @@ Route::MuteControllable::set_value (double val) if (!r) { return; } - + rl->push_back (r); _session.set_mute (rl, bval); } @@ -3190,7 +3198,7 @@ Route::MuteControllable::get_value () const if (!r) { return 0; } - + return r->muted() ? 1.0f : 0.0f; } @@ -3200,7 +3208,7 @@ Route::set_block_size (pframes_t nframes) for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->set_block_size (nframes); } - + _session.ensure_buffers (n_process_buffers ()); } @@ -3237,30 +3245,30 @@ void Route::shift (framepos_t pos, framecnt_t frames) { /* gain automation */ - { - boost::shared_ptr gc = _amp->gain_control(); - - XMLNode &before = gc->alist()->get_state (); - gc->alist()->shift (pos, frames); - XMLNode &after = gc->alist()->get_state (); - _session.add_command (new MementoCommand (*gc->alist().get(), &before, &after)); - } + { + boost::shared_ptr gc = _amp->gain_control(); + + XMLNode &before = gc->alist()->get_state (); + gc->alist()->shift (pos, frames); + XMLNode &after = gc->alist()->get_state (); + _session.add_command (new MementoCommand (*gc->alist().get(), &before, &after)); + } /* pan automation */ - { - ControlSet::Controls& c (_pannable->controls()); - - for (ControlSet::Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) { - boost::shared_ptr pc = boost::dynamic_pointer_cast (ci->second); - if (pc) { - boost::shared_ptr al = pc->alist(); - XMLNode& before = al->get_state (); - al->shift (pos, frames); - XMLNode& after = al->get_state (); - _session.add_command (new MementoCommand (*al.get(), &before, &after)); - } - } - } + if (_pannable) { + ControlSet::Controls& c (_pannable->controls()); + + for (ControlSet::Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) { + boost::shared_ptr pc = boost::dynamic_pointer_cast (ci->second); + if (pc) { + boost::shared_ptr al = pc->alist(); + XMLNode& before = al->get_state (); + al->shift (pos, frames); + XMLNode& after = al->get_state (); + _session.add_command (new MementoCommand (*al.get(), &before, &after)); + } + } + } /* redirect automation */ { @@ -3270,16 +3278,16 @@ Route::shift (framepos_t pos, framecnt_t frames) set parameters = (*i)->what_can_be_automated(); for (set::const_iterator p = parameters.begin (); p != parameters.end (); ++p) { - boost::shared_ptr ac = (*i)->automation_control (*p); - if (ac) { - boost::shared_ptr al = ac->alist(); - XMLNode &before = al->get_state (); - al->shift (pos, frames); - XMLNode &after = al->get_state (); - _session.add_command (new MementoCommand (*al.get(), &before, &after)); - } - } - } + boost::shared_ptr ac = (*i)->automation_control (*p); + if (ac) { + boost::shared_ptr al = ac->alist(); + XMLNode &before = al->get_state (); + al->shift (pos, frames); + XMLNode &after = al->get_state (); + _session.add_command (new MementoCommand (*al.get(), &before, &after)); + } + } + } } } @@ -3310,25 +3318,55 @@ Route::set_name (const string& str) ret = (_input->set_name(name) && _output->set_name(name)); if (ret) { + /* rename the main outs. Leave other IO processors + * with whatever name they already have, because its + * just fine as it is (it will not contain the route + * name if its a port insert, port send or port return). + */ + + if (_main_outs) { + if (_main_outs->set_name (name)) { + /* XXX returning false here is stupid because + we already changed the route name. + */ + return false; + } + } + } - Glib::RWLock::ReaderLock lm (_processor_lock); + return ret; +} - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { +/** 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); - /* rename all I/O processors that have inputs or outputs */ + XMLNodeList children = node.children(); + for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) { + + if ((*i)->name() == X_("IO")) { - boost::shared_ptr iop = boost::dynamic_pointer_cast (*i); + IO::set_name_in_state (**i, name); - if (iop && (iop->output() || iop->input())) { - if (!iop->set_name (name)) { - ret = false; - } + } 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); + + } } - - return ret; } boost::shared_ptr @@ -3358,7 +3396,7 @@ Route::set_phase_invert (uint32_t c, bool yn) if (_phase_invert[c] != yn) { _phase_invert[c] = yn; phase_invert_changed (); /* EMIT SIGNAL */ - _session.set_dirty (); + _session.set_dirty (); } } @@ -3368,7 +3406,7 @@ Route::set_phase_invert (boost::dynamic_bitset<> p) if (_phase_invert != p) { _phase_invert = p; phase_invert_changed (); /* EMIT SIGNAL */ - _session.set_dirty (); + _session.set_dirty (); } } @@ -3406,7 +3444,7 @@ Route::set_active (bool yn, void* src) _route_group->foreach_route (boost::bind (&Route::set_active, _1, yn, _route_group)); return; } - + if (_active != yn) { _active = yn; _input->set_active (yn); @@ -3446,7 +3484,7 @@ Route::pannable() const boost::shared_ptr Route::panner() const { - /* may be null ! */ + /* may be null ! */ return _main_outs->panner_shell()->panner(); } @@ -3520,7 +3558,7 @@ Route::nth_send (uint32_t n) if (n-- == 0) { return *i; } - } + } } return boost::shared_ptr (); @@ -3529,19 +3567,19 @@ Route::nth_send (uint32_t n) bool Route::has_io_processor_named (const string& name) { - Glib::RWLock::ReaderLock lm (_processor_lock); - ProcessorList::iterator i; - - for (i = _processors.begin(); i != _processors.end(); ++i) { - if (boost::dynamic_pointer_cast (*i) || - boost::dynamic_pointer_cast (*i)) { - if ((*i)->name() == name) { - return true; - } - } - } - - return false; + Glib::RWLock::ReaderLock lm (_processor_lock); + ProcessorList::iterator i; + + for (i = _processors.begin(); i != _processors.end(); ++i) { + if (boost::dynamic_pointer_cast (*i) || + boost::dynamic_pointer_cast (*i)) { + if ((*i)->name() == name) { + return true; + } + } + } + + return false; } MuteMaster::MutePoint @@ -3584,7 +3622,7 @@ list Route::unknown_processors () const { list p; - + Glib::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { @@ -3595,14 +3633,71 @@ Route::unknown_processors () const return p; } -void -Route::set_latency_ranges (bool playback) const + +framecnt_t +Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecnt_t our_latency) const +{ + /* we assume that all our input ports feed all our output ports. its not + universally true, but the alternative is way too corner-case to worry about. + */ + + jack_latency_range_t all_connections; + + if (from.empty()) { + all_connections.min = 0; + all_connections.max = 0; + } else { + all_connections.min = ~((jack_nframes_t) 0); + all_connections.max = 0; + + /* iterate over all "from" ports and determine the latency range for all of their + connections to the "outside" (outside of this Route). + */ + + for (PortSet::iterator p = from.begin(); p != from.end(); ++p) { + + jack_latency_range_t range; + + p->get_connected_latency_range (range, playback); + + all_connections.min = min (all_connections.min, range.min); + all_connections.max = max (all_connections.max, range.max); + } + } + + /* set the "from" port latencies to the max/min range of all their connections */ + + for (PortSet::iterator p = from.begin(); p != from.end(); ++p) { + p->set_private_latency_range (all_connections, playback); + } + + /* set the ports "in the direction of the flow" to the same value as above plus our own signal latency */ + + all_connections.min += our_latency; + all_connections.max += our_latency; + + for (PortSet::iterator p = to.begin(); p != to.end(); ++p) { + p->set_private_latency_range (all_connections, playback); + } + + return all_connections.max; +} + +framecnt_t +Route::set_private_port_latencies (bool playback) const { framecnt_t own_latency = 0; - /* Processor list not protected by lock: MUST BE CALLED FROM PROCESS THREAD OR - LATENCY CALLBACK - */ + /* Processor list not protected by lock: MUST BE CALLED FROM PROCESS THREAD + OR LATENCY CALLBACK. + + This is called (early) from the latency callback. It computes the REAL + latency associated with each port and stores the result as the "private" + latency of the port. A later call to Route::set_public_port_latencies() + sets all ports to the same value to reflect the fact that we do latency + compensation and so all signals are delayed by the same amount as they + flow through ardour. + */ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((*i)->active ()) { @@ -3610,58 +3705,41 @@ Route::set_latency_ranges (bool playback) const } } - if (playback) { - update_port_latencies (_input->ports (), _output->ports (), true, own_latency); - } else { - update_port_latencies (_output->ports (), _input->ports (), false, own_latency); - } -} - -void -Route::update_port_latencies (const PortSet& operands, const PortSet& feeders, bool playback, framecnt_t our_latency) const -{ -#ifdef HAVE_JACK_NEW_LATENCY - - /* we assume that all our input ports feed all our output ports. its not - universally true, but the alternative is way too corner-case to worry about. - */ - - jack_latency_range_t all_connections; - - all_connections.min = ~((jack_nframes_t) 0); - all_connections.max = 0; - - /* iterate over all feeder ports and determine their relevant latency, taking - the maximum and minimum across all of them. - */ - - for (PortSet::const_iterator p = feeders.begin(); p != feeders.end(); ++p) { - - jack_latency_range_t range; - - p->get_connected_latency_range (range, playback); - - all_connections.min = min (all_connections.min, range.min); - all_connections.max = max (all_connections.max, range.max); - } - - all_connections.min += our_latency; - all_connections.max += our_latency; - - for (PortSet::const_iterator p = operands.begin(); p != operands.end(); ++p) { - - p->set_latency_range (all_connections, playback); - - DEBUG_TRACE (DEBUG::Latency, string_compose ("Port %1 %5 latency range %2 .. %3 (including route latency of %4)\n", - p->name(), - all_connections.min, - all_connections.max, - our_latency, - (playback ? "PLAYBACK" : "CAPTURE"))); - } -#endif + if (playback) { + /* playback: propagate latency from "outside the route" to outputs to inputs */ + return update_port_latencies (_output->ports (), _input->ports (), true, own_latency); + } else { + /* capture: propagate latency from "outside the route" to inputs to outputs */ + return update_port_latencies (_input->ports (), _output->ports (), false, own_latency); + } } +void +Route::set_public_port_latencies (framecnt_t value, bool playback) const +{ + /* this is called to set the JACK-visible port latencies, which take + latency compensation into account. + */ + + jack_latency_range_t range; + + range.min = value; + range.max = value; + + { + const PortSet& ports (_input->ports()); + for (PortSet::const_iterator p = ports.begin(); p != ports.end(); ++p) { + p->set_public_latency_range (range, playback); + } + } + + { + const PortSet& ports (_output->ports()); + for (PortSet::const_iterator p = ports.begin(); p != ports.end(); ++p) { + p->set_public_latency_range (range, playback); + } + } +} /** Put the invisible processors in the right place in _processors. * Must be called with a writer lock on _processor_lock held. @@ -3670,21 +3748,21 @@ void Route::setup_invisible_processors () { #ifndef NDEBUG - Glib::RWLock::WriterLock lm (_processor_lock, Glib::TRY_LOCK); + Glib::RWLock::WriterLock lm (_processor_lock, Glib::TRY_LOCK); assert (!lm.locked ()); #endif - if (!_main_outs) { - /* too early to be doing this stuff */ - return; - } + if (!_main_outs) { + /* too early to be doing this stuff */ + return; + } /* we'll build this new list here and then use it */ - + ProcessorList new_processors; /* find visible processors */ - + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((*i)->display_to_user ()) { new_processors.push_back (*i); @@ -3718,10 +3796,10 @@ Route::setup_invisible_processors () new_processors.insert (amp, _meter); break; case MeterPostFader: - /* do nothing here */ + /* do nothing here */ break; case MeterOutput: - /* do nothing here */ + /* do nothing here */ break; case MeterCustom: /* the meter is visible, so we don't touch it here */ @@ -3731,60 +3809,63 @@ Route::setup_invisible_processors () /* MAIN OUTS */ - assert (_main_outs); - assert (!_main_outs->display_to_user ()); - new_processors.push_back (_main_outs); + assert (_main_outs); + assert (!_main_outs->display_to_user ()); + new_processors.push_back (_main_outs); - /* iterator for the main outs */ - - ProcessorList::iterator main = new_processors.end(); - --main; + /* iterator for the main outs */ - /* OUTPUT METERING */ + ProcessorList::iterator main = new_processors.end(); + --main; - if (_meter && (_meter_point == MeterOutput || _meter_point == MeterPostFader)) { - assert (!_meter->display_to_user ()); + /* OUTPUT METERING */ - /* add the processor just before or just after the main outs */ - - ProcessorList::iterator meter_point = main; + if (_meter && (_meter_point == MeterOutput || _meter_point == MeterPostFader)) { + assert (!_meter->display_to_user ()); - if (_meter_point == MeterOutput) { - ++meter_point; - } - new_processors.insert (meter_point, _meter); - } + /* add the processor just before or just after the main outs */ + + ProcessorList::iterator meter_point = main; + + if (_meter_point == MeterOutput) { + ++meter_point; + } + new_processors.insert (meter_point, _meter); + } /* MONITOR SEND */ if (_monitor_send && !is_monitor ()) { assert (!_monitor_send->display_to_user ()); - if (Config->get_solo_control_is_listen_control()) { - switch (Config->get_listen_position ()) { - case PreFaderListen: - switch (Config->get_pfl_position ()) { - case PFLFromBeforeProcessors: - new_processors.push_front (_monitor_send); - break; - case PFLFromAfterProcessors: - new_processors.insert (amp, _monitor_send); - break; - } - break; - case AfterFaderListen: - switch (Config->get_afl_position ()) { - case AFLFromBeforeProcessors: - new_processors.insert (after_amp, _monitor_send); - break; - case AFLFromAfterProcessors: - new_processors.insert (new_processors.end(), _monitor_send); - break; - } - break; - } - } else { - new_processors.insert (new_processors.end(), _monitor_send); - } + if (Config->get_solo_control_is_listen_control()) { + switch (Config->get_listen_position ()) { + case PreFaderListen: + switch (Config->get_pfl_position ()) { + case PFLFromBeforeProcessors: + new_processors.push_front (_monitor_send); + break; + case PFLFromAfterProcessors: + new_processors.insert (amp, _monitor_send); + break; + } + _monitor_send->set_can_pan (false); + break; + case AfterFaderListen: + switch (Config->get_afl_position ()) { + case AFLFromBeforeProcessors: + new_processors.insert (after_amp, _monitor_send); + break; + case AFLFromAfterProcessors: + new_processors.insert (new_processors.end(), _monitor_send); + break; + } + _monitor_send->set_can_pan (true); + break; + } + } else { + new_processors.insert (new_processors.end(), _monitor_send); + _monitor_send->set_can_pan (false); + } } /* MONITOR CONTROL */ @@ -3793,7 +3874,7 @@ Route::setup_invisible_processors () assert (!_monitor_control->display_to_user ()); new_processors.push_front (_monitor_control); } - + /* INTERNAL RETURN */ /* doing this here means that any monitor control will come just after @@ -3806,7 +3887,7 @@ Route::setup_invisible_processors () } /* EXPORT PROCESSOR */ - + if (_capturing_processor) { assert (!_capturing_processor->display_to_user ()); new_processors.push_front (_capturing_processor); @@ -3820,18 +3901,78 @@ Route::setup_invisible_processors () } } -bool -Route::should_monitor () const +void +Route::unpan () { - switch (Config->get_monitoring_model()) { - case HardwareMonitoring: - case ExternalMonitoring: - return !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording()); - break; - default: - break; + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::RWLock::ReaderLock lp (_processor_lock); + + _pannable.reset (); + + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr d = boost::dynamic_pointer_cast(*i); + if (d) { + d->unpan (); + } } +} - return true; +/** 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; +}