X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=5df58ea846b63669d26722f4e07449d5138dd346;hb=bae86a2d908122d0bb54afcce82d2cf232268a8a;hp=951a4ff30c6cbf3184374d105e0988a960779dba;hpb=21ca6a10a96c135e7435f1cc786f4395020ca232;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 951a4ff30c..5df58ea846 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -47,6 +47,7 @@ #include "ardour/internal_return.h" #include "ardour/internal_send.h" #include "ardour/meter.h" +#include "ardour/delayline.h" #include "ardour/midi_buffer.h" #include "ardour/midi_port.h" #include "ardour/monitor_processor.h" @@ -79,6 +80,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , GraphNode (sess._process_graph) , _active (true) , _signal_latency (0) + , _signal_latency_at_amp_position (0) , _initial_delay (0) , _roll_delay (0) , _flags (flg) @@ -100,6 +102,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _order_key (0) , _has_order_key (false) , _remote_control_id (0) + , _track_number (0) , _in_configure_processors (false) , _initial_io_setup (false) , _custom_meter_position_noted (false) @@ -142,6 +145,11 @@ Route::init () _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2)); _output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1)); + if (!is_master() && !is_monitor() && !is_auditioner()) { + _delayline.reset (new DelayLine (_session, _name)); + add_processor (_delayline, PreFader); + } + /* add amp processor */ _amp.reset (new Amp (_session)); @@ -180,8 +188,7 @@ Route::init () Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this))); { - /* run a configure so that the invisible processors get set up */ - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); configure_processors (0); } @@ -420,10 +427,19 @@ Route::process_output_buffers (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick, bool gain_automation_ok) { + /* Caller must hold process lock */ + assert (!AudioEngine::instance()->process_lock().trylock()); + + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); + assert(lm.locked()); + /* figure out if we're going to use gain automation */ if (gain_automation_ok) { _amp->set_gain_automation_buffer (_session.gain_automation_buffer ()); - _amp->setup_gain_automation (start_frame, end_frame, nframes); + _amp->setup_gain_automation ( + start_frame + _signal_latency_at_amp_position, + end_frame + _signal_latency_at_amp_position, + nframes); } else { _amp->apply_gain_automation (false); } @@ -501,6 +517,8 @@ Route::process_output_buffers (BufferSet& bufs, /* set this to be true if the meter will already have been ::run() earlier */ bool const meter_already_run = metering_state() == MeteringInput; + framecnt_t latency = 0; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (meter_already_run && boost::dynamic_pointer_cast (*i)) { @@ -526,9 +544,118 @@ Route::process_output_buffers (BufferSet& bufs, do we catch route != active somewhere higher? */ - (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back()); + if (boost::dynamic_pointer_cast(*i) != 0) { + boost::dynamic_pointer_cast(*i)->set_delay_in(_signal_latency - latency); + } + + (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back()); bufs.set_count ((*i)->output_streams()); + + if ((*i)->active ()) { + latency += (*i)->signal_latency (); + } + } +} + +void +Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, + boost::shared_ptr endpoint, + bool include_endpoint, bool for_export, bool for_freeze) +{ + /* If no processing is required, there's no need to go any further. */ + if (!endpoint && !include_endpoint) { + return; + } + + framecnt_t latency = bounce_get_latency(_amp, false, for_export, for_freeze); + _amp->set_gain_automation_buffer (_session.gain_automation_buffer ()); + _amp->setup_gain_automation (start - latency, start - latency + nframes, nframes); + + latency = 0; + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + + if (!include_endpoint && (*i) == endpoint) { + break; + } + + /* if we're not exporting, stop processing if we come across a routing processor. */ + if (!for_export && boost::dynamic_pointer_cast(*i)) { + break; + } + if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) { + break; + } + + /* don't run any processors that does routing. + * oh, and don't bother with the peak meter either. + */ + if (!(*i)->does_routing() && !boost::dynamic_pointer_cast(*i)) { + (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true); + buffers.set_count ((*i)->output_streams()); + latency += (*i)->signal_latency (); + } + + if ((*i) == endpoint) { + break; + } + } +} + +framecnt_t +Route::bounce_get_latency (boost::shared_ptr endpoint, + bool include_endpoint, bool for_export, bool for_freeze) const +{ + framecnt_t latency = 0; + if (!endpoint && !include_endpoint) { + return latency; + } + + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if (!include_endpoint && (*i) == endpoint) { + break; + } + if (!for_export && boost::dynamic_pointer_cast(*i)) { + break; + } + if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) { + break; + } + if (!(*i)->does_routing() && !boost::dynamic_pointer_cast(*i)) { + latency += (*i)->signal_latency (); + } + if ((*i) == endpoint) { + break; + } } + return latency; +} + +ChanCount +Route::bounce_get_output_streams (ChanCount &cc, boost::shared_ptr endpoint, + bool include_endpoint, bool for_export, bool for_freeze) const +{ + if (!endpoint && !include_endpoint) { + return cc; + } + + for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { + if (!include_endpoint && (*i) == endpoint) { + break; + } + if (!for_export && boost::dynamic_pointer_cast(*i)) { + break; + } + if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) { + break; + } + if (!(*i)->does_routing() && !boost::dynamic_pointer_cast(*i)) { + cc = (*i)->output_streams(); + } + if ((*i) == endpoint) { + break; + } + } + return cc; } ChanCount @@ -542,6 +669,7 @@ Route::monitor_run (framepos_t start_frame, framepos_t end_frame, pframes_t nfra { assert (is_monitor()); BufferSet& bufs (_session.get_route_buffers (n_process_buffers())); + fill_buffers_with_input (bufs, _input, nframes); passthru (bufs, start_frame, end_frame, nframes, declick); } @@ -945,6 +1073,7 @@ Route::add_processor (boost::shared_ptr processor, boost::shared_ptr< } { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); @@ -986,8 +1115,6 @@ Route::add_processor (boost::shared_ptr processor, boost::shared_ptr< // configure redirect ports properly, etc. { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - if (configure_processors_unlocked (err)) { pstate.restore (); configure_processors_unlocked (0); // it worked before we tried to add it ... @@ -1068,7 +1195,8 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) } else if (node.name() == "Send") { - processor.reset (new Send (_session, _pannable, _mute_master)); + boost::shared_ptr sendpan (new Pannable (_session)); + processor.reset (new Send (_session, sendpan, _mute_master)); } else { @@ -1115,6 +1243,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr } { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); @@ -1137,8 +1266,8 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr (*i)->activate (); } + /* Think: does this really need to be called for every processor in the loop? */ { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (configure_processors_unlocked (err)) { pstate.restore (); configure_processors_unlocked (0); // it worked before we tried to add it ... @@ -1314,6 +1443,7 @@ Route::clear_processors (Placement p) } { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorList new_list; ProcessorStreams err; @@ -1325,7 +1455,7 @@ Route::clear_processors (Placement p) seen_amp = true; } - if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) { + if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs || (*i) == _delayline) { /* you can't remove these */ @@ -1358,11 +1488,7 @@ Route::clear_processors (Placement p) } _processors = new_list; - - { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - configure_processors_unlocked (&err); // this can't fail - } + configure_processors_unlocked (&err); // this can't fail } processor_max_streams.reset(); @@ -1382,12 +1508,21 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream { // TODO once the export point can be configured properly, do something smarter here if (processor == _capturing_processor) { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK); + if (need_process_lock) { + lx.acquire(); + } + _capturing_processor.reset(); + + if (need_process_lock) { + lx.release(); + } } /* these can never be removed */ - if (processor == _amp || processor == _meter || processor == _main_outs) { + if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) { return 0; } @@ -1398,7 +1533,16 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream processor_max_streams.reset(); { - Glib::Threads::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK); + if (need_process_lock) { + lx.acquire(); + } + + /* Caller must hold process lock */ + assert (!AudioEngine::instance()->process_lock().trylock()); + + Glib::Threads::RWLock::WriterLock lm (_processor_lock); // XXX deadlock after export + ProcessorState pstate (this); ProcessorList::iterator i; @@ -1438,22 +1582,11 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream return 1; } - if (need_process_lock) { - Glib::Threads::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 :) */ - configure_processors_unlocked (0); - return -1; - } + if (configure_processors_unlocked (err)) { + pstate.restore (); + /* we know this will work, because it worked before :) */ + configure_processors_unlocked (0); + return -1; } _have_internal_generator = false; @@ -1468,6 +1601,9 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream } } } + if (need_process_lock) { + lx.release(); + } } reset_instrument_info (); @@ -1490,6 +1626,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* processor_max_streams.reset(); { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); @@ -1502,7 +1639,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* /* these can never be removed */ - if (processor == _amp || processor == _meter || processor == _main_outs) { + if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) { ++i; continue; } @@ -1536,16 +1673,13 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* _output->set_user_latency (0); - { - Glib::Threads::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; - } + if (configure_processors_unlocked (err)) { + pstate.restore (); + /* we know this will work, because it worked before :) */ + configure_processors_unlocked (0); + return -1; } + //lx.unlock(); _have_internal_generator = false; @@ -1574,51 +1708,6 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* return 0; } -void -Route::set_custom_panner_uri (std::string const panner_uri) -{ - if (!_main_outs->panner_shell()->set_user_selected_panner_uri(panner_uri)) { - DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1 '%2' -- no change needed\n"), name(), panner_uri)); - /* no change needed */ - return; - } - - DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1 '%2' -- reconfigure I/O\n"), name(), panner_uri)); - - if (_in_configure_processors) { - DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1' -- called while in_configure_processors\n"), name())); - return; - } - - /* reconfigure I/O */ - { - Glib::Threads::RWLock::WriterLock lm (_processor_lock); - ProcessorState pstate (this); - if (panner()) - { - /* there is already a panner it can just be re-configured in-place */ - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - ChanCount in = panner()->in(); - ChanCount out = panner()->out(); - _main_outs->panner_shell()->configure_io(in, out); - _main_outs->panner_shell()->pannable()->set_panner(panner()); - } - else - { - Glib::Threads::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 ... - return; - } - } - } - - processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ - _session.set_dirty (); -} - void Route::reset_instrument_info () { @@ -1632,7 +1721,9 @@ Route::reset_instrument_info () int Route::configure_processors (ProcessorStreams* err) { +#ifndef PLATFORM_WINDOWS assert (!AudioEngine::instance()->process_lock().trylock()); +#endif if (!_in_configure_processors) { Glib::Threads::RWLock::WriterLock lm (_processor_lock); @@ -1703,7 +1794,9 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) int Route::configure_processors_unlocked (ProcessorStreams* err) { +#ifndef PLATFORM_WINDOWS assert (!AudioEngine::instance()->process_lock().trylock()); +#endif if (_in_configure_processors) { return 0; @@ -1804,6 +1897,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err */ { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); @@ -1865,13 +1959,9 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err /* If the meter is in a custom position, find it and make a rough note of its position */ maybe_note_meter_position (); - { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - - if (configure_processors_unlocked (err)) { - pstate.restore (); - return -1; - } + if (configure_processors_unlocked (err)) { + pstate.restore (); + return -1; } } @@ -2514,6 +2604,9 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "meter") { _meter->set_state (**niter, Stateful::current_state_version); new_order.push_back (_meter); + } else if (prop->value() == "delay") { + _delayline->set_state (**niter, Stateful::current_state_version); + new_order.push_back (_delayline); } else if (prop->value() == "main-outs") { _main_outs->set_state (**niter, Stateful::current_state_version); } else if (prop->value() == "intreturn") { @@ -2551,7 +2644,7 @@ Route::set_processor_state (const XMLNode& node) if (prop->value() == "intsend") { - processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr(), Delivery::Role (0))); + processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast(shared_from_this()), boost::shared_ptr(), Delivery::Aux, true)); } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || @@ -2567,7 +2660,7 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "send") { - processor.reset (new Send (_session, _pannable, _mute_master)); + processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true)); } else { error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; @@ -2598,11 +2691,11 @@ Route::set_processor_state (const XMLNode& node) } { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); _processors = new_order; if (must_configure) { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); configure_processors_unlocked (0); } @@ -2716,10 +2809,11 @@ Route::enable_monitor_send () /* master never sends to monitor section via the normal mechanism */ assert (!is_master ()); + assert (!is_monitor ()); /* make sure we have one */ if (!_monitor_send) { - _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen)); + _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast(shared_from_this()), _session.monitor_out(), Delivery::Listen)); _monitor_send->set_display_to_user (false); } @@ -2756,7 +2850,8 @@ Route::add_aux_send (boost::shared_ptr route, boost::shared_ptrprocess_lock ()); - listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux)); + boost::shared_ptr sendpan (new Pannable (_session)); + listener.reset (new InternalSend (_session, sendpan, _mute_master, boost::dynamic_pointer_cast(shared_from_this()), route, Delivery::Aux)); } add_processor (listener, before); @@ -2843,7 +2938,7 @@ Route::feeds (boost::shared_ptr other, bool* via_sends_only) { const FedBy& fed_by (other->fed_by()); - for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) { + for (FedBy::const_iterator f = fed_by.begin(); f != fed_by.end(); ++f) { boost::shared_ptr sr = f->r.lock(); if (sr && (sr.get() == this)) { @@ -3116,6 +3211,7 @@ Route::set_meter_point (MeterPoint p, bool force) bool meter_was_visible_to_user = _meter->display_to_user (); { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); maybe_note_meter_position (); @@ -3183,17 +3279,14 @@ void Route::listen_position_changed () { { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); - { - Glib::Threads::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 ... - return; - } + if (configure_processors_unlocked (0)) { + pstate.restore (); + configure_processors_unlocked (0); // it worked before we tried to add it ... + return; } } @@ -3204,15 +3297,16 @@ Route::listen_position_changed () boost::shared_ptr Route::add_export_point() { + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); if (!_capturing_processor) { + lm.release(); + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + Glib::Threads::RWLock::WriterLock lw (_processor_lock); _capturing_processor.reset (new CapturingProcessor (_session)); _capturing_processor->activate (); - { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - configure_processors (0); - } + configure_processors_unlocked (0); } @@ -3223,15 +3317,24 @@ framecnt_t Route::update_signal_latency () { framecnt_t l = _output->user_latency(); + framecnt_t lamp = 0; + bool before_amp = true; for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if ((*i)->active ()) { l += (*i)->signal_latency (); } + if ((*i) == _amp) { + before_amp = false; + } + if (before_amp) { + lamp = l; + } } DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l)); + _signal_latency_at_amp_position = lamp; if (_signal_latency != l) { _signal_latency = l; signal_latency_changed (); /* EMIT SIGNAL */ @@ -3460,14 +3563,14 @@ Route::save_as_template (const string& path, const string& name) bool Route::set_name (const string& str) { - bool ret; - string ioproc_name; - string name; + if (str == name()) { + return true; + } - name = Route::ensure_track_or_route_name (str, _session); + string name = Route::ensure_track_or_route_name (str, _session); SessionObject::set_name (name); - ret = (_input->set_name(name) && _output->set_name(name)); + bool ret = (_input->set_name(name) && _output->set_name(name)); if (ret) { /* rename the main outs. Leave other IO processors @@ -3592,6 +3695,10 @@ Route::denormal_protection () const void Route::set_active (bool yn, void* src) { + if (_session.transport_rolling()) { + return; + } + if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_route_active()) { _route_group->foreach_route (boost::bind (&Route::set_active, _1, yn, _route_group)); return; @@ -3942,7 +4049,7 @@ Route::setup_invisible_processors () ++amp; } - assert (amp != _processors.end ()); + assert (amp != new_processors.end ()); /* and the processor after the amp */ @@ -4034,6 +4141,10 @@ Route::setup_invisible_processors () } } + if (!is_master() && !is_monitor() && !is_auditioner()) { + new_processors.push_front (_delayline); + } + /* MONITOR CONTROL */ if (_monitor_control && is_monitor ()) { @@ -4163,7 +4274,7 @@ Route::has_external_redirects () const boost::shared_ptr Route::the_instrument () const { - Glib::Threads::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); return the_instrument_unlocked (); } @@ -4190,8 +4301,13 @@ Route::non_realtime_locate (framepos_t pos) _pannable->transport_located (pos); } + if (_delayline.get()) { + _delayline.get()->flush(); + } + { - Glib::Threads::RWLock::WriterLock lm (_processor_lock); + //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->transport_located (pos);