+
+
+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;
+
+ 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.
+
+ 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 ()) {
+ own_latency += (*i)->signal_latency ();
+ }
+ }
+
+ 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.
+ */
+void
+Route::setup_invisible_processors ()
+{
+#ifndef NDEBUG
+ Glib::RWLock::WriterLock lm (_processor_lock, Glib::TRY_LOCK);
+ assert (!lm.locked ());
+#endif
+
+ 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);
+ }
+ }
+
+ /* find the amp */
+
+ ProcessorList::iterator amp = new_processors.begin ();
+ while (amp != new_processors.end() && boost::dynamic_pointer_cast<Amp> (*amp) == 0) {
+ ++amp;
+ }
+
+ assert (amp != _processors.end ());
+
+ /* and the processor after the amp */
+
+ ProcessorList::iterator after_amp = amp;
+ ++after_amp;
+
+ /* METER */
+
+ if (_meter) {
+ switch (_meter_point) {
+ case MeterInput:
+ assert (!_meter->display_to_user ());
+ new_processors.push_front (_meter);
+ break;
+ case MeterPreFader:
+ assert (!_meter->display_to_user ());
+ new_processors.insert (amp, _meter);
+ break;
+ case MeterPostFader:
+ /* do nothing here */
+ break;
+ case MeterOutput:
+ /* do nothing here */
+ break;
+ case MeterCustom:
+ /* the meter is visible, so we don't touch it here */
+ break;
+ }
+ }
+
+ /* 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;
+
+ /* OUTPUT METERING */
+
+ if (_meter && (_meter_point == MeterOutput || _meter_point == MeterPostFader)) {
+ assert (!_meter->display_to_user ());
+
+ /* 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;
+ }
+ _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 */
+
+ if (_monitor_control && is_monitor ()) {
+ 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
+ the return.
+ */
+
+ if (_intreturn) {
+ assert (!_intreturn->display_to_user ());
+ new_processors.push_front (_intreturn);
+ }
+
+ /* EXPORT PROCESSOR */
+
+ if (_capturing_processor) {
+ assert (!_capturing_processor->display_to_user ());
+ new_processors.push_front (_capturing_processor);
+ }
+
+ _processors = new_processors;
+
+ DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: setup_invisible_processors\n", _name));
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1\n", (*i)->name ()));
+ }
+}
+
+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 ()
+{
+ 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<Delivery> d = boost::dynamic_pointer_cast<Delivery>(*i);
+ if (d) {
+ d->unpan ();
+ }
+ }
+}