+/** Called when there is a proposed change to the input port count */
+bool
+Route::input_port_count_changing (ChanCount to)
+{
+ list<pair<ChanCount, ChanCount> > c = try_configure_processors (to, 0);
+ if (c.empty()) {
+ /* The processors cannot be configured with the new input arrangement, so
+ block the change.
+ */
+ return true;
+ }
+
+ /* The change is ok */
+ return false;
+}
+
+/** Called when there is a proposed change to the output port count */
+bool
+Route::output_port_count_changing (ChanCount to)
+{
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ if (processor_out_streams.get(*t) > to.get(*t)) {
+ return true;
+ }
+ }
+ /* The change is ok */
+ return false;
+}
+
+list<string>
+Route::unknown_processors () const
+{
+ list<string> p;
+
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
+ p.push_back ((*i)->name ());
+ }
+ }
+
+ return p;
+}
+
+
+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.
+ */
+
+ LatencyRange all_connections;
+
+ if (from.empty()) {
+ all_connections.min = 0;
+ all_connections.max = 0;
+ } else {
+ all_connections.min = ~((pframes_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) {
+
+ LatencyRange 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.
+ */
+
+ LatencyRange 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::Threads::RWLock::WriterLock lm (_processor_lock, Glib::Threads::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 ()));
+ }
+}
+
+void
+Route::unpan ()
+{
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ Glib::Threads::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 ();
+ }
+ }
+}
+
+/** 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<PeakMeter> (*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<Processor>
+Route::processor_by_id (PBD::ID id) const
+{
+ Glib::Threads::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<Processor> ();
+}
+
+/** @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;
+}
+
+bool
+Route::has_external_redirects () const
+{
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+ /* ignore inactive processors and obviously ignore the main
+ * outs since everything has them and we don't care.
+ */
+
+ if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) {
+ return true;;
+ }
+ }
+
+ return false;
+}
+
+boost::shared_ptr<Processor>
+Route::the_instrument () const
+{
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+ return the_instrument_unlocked ();
+}
+
+boost::shared_ptr<Processor>
+Route::the_instrument_unlocked () const
+{
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (boost::dynamic_pointer_cast<PluginInsert>(*i)) {
+ if ((*i)->input_streams().n_midi() > 0 &&
+ (*i)->output_streams().n_audio() > 0) {
+ return (*i);
+ }
+ }
+ }
+ return boost::shared_ptr<Processor>();
+}
+
+
+
+void
+Route::non_realtime_locate (framepos_t pos)
+{
+ if (_pannable) {
+ _pannable->transport_located (pos);
+ }
+
+ {
+ //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+ Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ (*i)->transport_located (pos);
+ }
+ }
+}
+
+void
+Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pframes_t nframes)
+{
+ size_t n_buffers;
+ size_t i;
+
+ /* MIDI
+ *
+ * We don't currently mix MIDI input together, so we don't need the
+ * complex logic of the audio case.
+ */
+
+ n_buffers = bufs.count().n_midi ();
+
+ for (i = 0; i < n_buffers; ++i) {
+
+ boost::shared_ptr<MidiPort> source_port = io->midi (i);
+ MidiBuffer& buf (bufs.get_midi (i));
+
+ if (source_port) {
+ buf.copy (source_port->get_midi_buffer(nframes));
+ } else {
+ buf.silence (nframes);
+ }
+ }
+
+ /* AUDIO */
+
+ n_buffers = bufs.count().n_audio();
+
+ size_t n_ports = io->n_ports().n_audio();
+ float scaling = 1.0f;
+
+ if (n_ports > n_buffers) {
+ scaling = ((float) n_buffers) / n_ports;
+ }
+
+ for (i = 0; i < n_ports; ++i) {
+
+ /* if there are more ports than buffers, map them onto buffers
+ * in a round-robin fashion
+ */
+
+ boost::shared_ptr<AudioPort> source_port = io->audio (i);
+ AudioBuffer& buf (bufs.get_audio (i%n_buffers));
+
+
+ if (i < n_buffers) {
+
+ /* first time through just copy a channel into
+ the output buffer.
+ */
+
+ buf.read_from (source_port->get_audio_buffer (nframes), nframes);
+
+ if (scaling != 1.0f) {
+ buf.apply_gain (scaling, nframes);
+ }
+
+ } else {
+
+ /* on subsequent times around, merge data from
+ * the port with what is already there
+ */
+
+ if (scaling != 1.0f) {
+ buf.accumulate_with_gain_from (source_port->get_audio_buffer (nframes), nframes, 0, scaling);
+ } else {
+ buf.accumulate_from (source_port->get_audio_buffer (nframes), nframes);
+ }
+ }
+ }
+
+ /* silence any remaining buffers */
+
+ for (; i < n_buffers; ++i) {
+ AudioBuffer& buf (bufs.get_audio (i));
+ buf.silence (nframes);
+ }
+
+ /* establish the initial setup of the buffer set, reflecting what was
+ copied into it. unless, of course, we are the auditioner, in which
+ case nothing was fed into it from the inputs at all.
+ */
+
+ if (!is_auditioner()) {
+ bufs.set_count (io->n_ports());
+ }
+}