make i18n build work ; add mackie dir back to build ; token work on amp for MIDI...
[ardour.git] / libs / ardour / route.cc
index e7d68c488438d55636b15c050d798d287d877180..56311c975db1063cb0fe929bbfd11cab0ec59c83 100644 (file)
 #include "ardour/buffer_set.h"
 #include "ardour/configuration.h"
 #include "ardour/cycle_timer.h"
+#include "ardour/delivery.h"
 #include "ardour/dB.h"
+#include "ardour/internal_send.h"
+#include "ardour/internal_return.h"
 #include "ardour/ladspa_plugin.h"
 #include "ardour/meter.h"
 #include "ardour/mix.h"
@@ -61,7 +64,7 @@ using namespace ARDOUR;
 using namespace PBD;
 
 uint32_t Route::order_key_cnt = 0;
-sigc::signal<void,const char*> Route::SyncOrderKeys;
+sigc::signal<void, string const &> Route::SyncOrderKeys;
 
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
@@ -73,6 +76,24 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
          
 {
        init ();
+       
+       /* add standard processors other than amp (added by ::init()) */
+       
+       _meter.reset (new PeakMeter (_session));
+       add_processor (_meter, PreFader);
+
+       if (_flags & ControlOut) {
+               /* where we listen to tracks */
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
+       }
+       
+       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
+       add_processor (_main_outs, PostFader);
+
+       /* now that we have _meter, its safe to connect to this */
+
+       _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
 }
 
 Route::Route (Session& sess, const XMLNode& node, DataType default_type)
@@ -83,17 +104,24 @@ Route::Route (Session& sess, const XMLNode& node, DataType default_type)
        , _default_type (default_type)
 {
        init ();
+
        _set_state (node, false);
+
+       /* now that we have _meter, its safe to connect to this */
+       
+       _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
 }
 
 void
 Route::init ()
 {
+       _solo_level = 0;
+       _solo_isolated = false;
        _active = true;
        processor_max_streams.reset();
        _solo_safe = false;
        _recordable = true;
-       order_keys[strdup (N_("signal"))] = order_key_cnt++;
+       order_keys[N_("signal")] = order_key_cnt++;
        _silent = false;
        _meter_point = MeterPostFader;
        _initial_delay = 0;
@@ -104,8 +132,7 @@ Route::init ()
        _remote_control_id = 0;
        _in_configure_processors = false;
        
-       _edit_group = 0;
-       _mix_group = 0;
+       _route_group = 0;
 
        _phase_invert = 0;
        _denormal_protection = false;
@@ -123,20 +150,10 @@ Route::init ()
        _input->changed.connect (mem_fun (this, &Route::input_change_handler));
        _output->changed.connect (mem_fun (this, &Route::output_change_handler));
 
-       /* add standard processors */
+       /* add amp processor  */
 
        _amp.reset (new Amp (_session, _mute_master));
        add_processor (_amp, PostFader);
-
-       _meter.reset (new PeakMeter (_session));
-       add_processor (_meter, PreFader);
-       
-       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
-       add_processor (_main_outs, PostFader);
-
-       /* now we can meter */
-       
-       _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
 }
 
 Route::~Route ()
@@ -145,10 +162,6 @@ Route::~Route ()
 
        clear_processors (PreFader);
        clear_processors (PostFader);
-
-       for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) {
-               free ((void*)(i->first));
-       }
 }
 
 void
@@ -167,23 +180,20 @@ Route::remote_control_id() const
 }
 
 long
-Route::order_key (const char* name) const
+Route::order_key (std::string const & name) const
 {
-       OrderKeys::const_iterator i;
-
-       for (i = order_keys.begin(); i != order_keys.end(); ++i) {
-               if (!strcmp (name, i->first)) {
-                       return i->second;
-               }
+       OrderKeys::const_iterator i = order_keys.find (name);
+       if (i == order_keys.end()) {
+               return -1;
        }
 
-       return -1;
+       return i->second;
 }
 
 void
-Route::set_order_key (const char* name, long n)
+Route::set_order_key (std::string const & name, long n)
 {
-       order_keys[strdup(name)] = n;
+       order_keys[name] = n;
 
        if (Config->get_sync_all_route_ordering()) {
                for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) {
@@ -194,8 +204,12 @@ Route::set_order_key (const char* name, long n)
        _session.set_dirty ();
 }
 
+/** Set all order keys to be the same as that for `base', if such a key
+ *  exists in this route.
+ *  @param base Base key.
+ */
 void
-Route::sync_order_keys (const char* base)
+Route::sync_order_keys (std::string const & base)
 {
        if (order_keys.empty()) {
                return;
@@ -242,10 +256,10 @@ Route::inc_gain (gain_t fraction, void *src)
 void
 Route::set_gain (gain_t val, void *src)
 {
-       if (src != 0 && _mix_group && src != _mix_group && _mix_group->is_active()) {
+       if (src != 0 && _route_group && src != _route_group && _route_group->active_property (RouteGroup::Gain)) {
                
-               if (_mix_group->is_relative()) {
-                       
+               if (_route_group->is_relative()) {
+
                        gain_t usable_gain = _amp->gain();
                        if (usable_gain < 0.000001f) {
                                usable_gain = 0.000001f;
@@ -264,24 +278,24 @@ Route::set_gain (gain_t val, void *src)
                        gain_t factor = delta / usable_gain;
 
                        if (factor > 0.0f) {
-                               factor = _mix_group->get_max_factor(factor);
+                               factor = _route_group->get_max_factor(factor);
                                if (factor == 0.0f) {
                                        _amp->gain_control()->Changed(); /* EMIT SIGNAL */
                                        return;
                                }
                        } else {
-                               factor = _mix_group->get_min_factor(factor);
+                               factor = _route_group->get_min_factor(factor);
                                if (factor == 0.0f) {
                                        _amp->gain_control()->Changed(); /* EMIT SIGNAL */
                                        return;
                                }
                        }
                                        
-                       _mix_group->apply (&Route::inc_gain, factor, _mix_group);
+                       _route_group->apply (&Route::inc_gain, factor, _route_group);
 
                } else {
                        
-                       _mix_group->apply (&Route::set_gain, val, _mix_group);
+                       _route_group->apply (&Route::set_gain, val, _route_group);
                }
 
                return;
@@ -307,7 +321,7 @@ Route::set_gain (gain_t val, void *src)
 void
 Route::process_output_buffers (BufferSet& bufs,
                               sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
-                              bool with_processors, int declick)
+                              bool /*with_processors*/, int declick)
 {
        bool monitor;
 
@@ -316,7 +330,7 @@ Route::process_output_buffers (BufferSet& bufs,
        switch (Config->get_monitoring_model()) {
        case HardwareMonitoring:
        case ExternalMonitoring:
-               monitor = record_enabled() && (_session.config.get_auto_input() || _session.actively_recording());
+               monitor = !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording());
                break;
        default:
                monitor = true;
@@ -327,13 +341,13 @@ Route::process_output_buffers (BufferSet& bufs,
        }
 
        /* figure out if we're going to use gain automation */
-
        _amp->setup_gain_automation (start_frame, end_frame, nframes);
        
-       /* tell main outs what to do about monitoring */
 
+       /* tell main outs what to do about monitoring */
        _main_outs->no_outs_cuz_we_no_monitor (!monitor);
 
+
        /* -------------------------------------------------------------------------------------------
           GLOBAL DECLICK (for transport changes etc.)
           ----------------------------------------------------------------------------------------- */
@@ -437,14 +451,26 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
        }
        
        bufs.set_count (_input->n_ports());
-       
-       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+
+       if (is_control() && _session.listening()) {
                
-               BufferSet::iterator o = bufs.begin(*t);
-               PortSet& ports (_input->ports());
+               /* control/monitor bus ignores input ports when something is
+                  feeding the listen "stream". data will "arrive" into the
+                  route from the intreturn processor element.
+               */
+                       
+               bufs.silence (nframes, 0);
 
-               for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
-                       o->read_from (i->get_buffer(nframes), nframes);
+       } else {
+       
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       
+                       BufferSet::iterator o = bufs.begin(*t);
+                       PortSet& ports (_input->ports());
+                       
+                       for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
+                               o->read_from (i->get_buffer(nframes), nframes);
+                       }
                }
        }
 
@@ -457,54 +483,103 @@ Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t n
        process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
 }
 
+void
+Route::set_listen (bool yn, void* src)
+{
+       if (_control_outs) { 
+               if (yn != _control_outs->active()) {
+                       if (yn) {
+                               _control_outs->activate ();
+                       } else {
+                               _control_outs->deactivate ();
+                       }
+
+                       listen_changed (src); /* EMIT SIGNAL */
+               }
+       }
+}
+
+bool
+Route::listening () const
+{
+       if (_control_outs) {
+               return _control_outs->active ();
+       } else {
+               return false;
+       }
+}
+
 void
 Route::set_solo (bool yn, void *src)
 {
-       if (_solo_safe) {
+       if (_solo_safe || _solo_isolated) {
                return;
        }
 
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_solo, yn, _mix_group);
+       if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
+               _route_group->apply (&Route::set_solo, yn, _route_group);
                return;
        }
 
-       if (_main_outs->soloed() != yn) {
-               _main_outs->mod_solo_level (yn ? 1 : -1);
+       if (soloed() != yn) {
+               mod_solo_level (yn ? 1 : -1);
                solo_changed (src); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }       
 }
 
-bool
-Route::soloed() const
+void
+Route::mod_solo_level (int32_t delta)
 {
-       return _main_outs->soloed ();
+       if (delta < 0) {
+               if (_solo_level >= (uint32_t) delta) {
+                       _solo_level += delta;
+               } else {
+                       _solo_level = 0;
+               }
+       } else {
+               _solo_level += delta;
+       }
+
+       /* tell main outs what the solo situation is
+        */
+
+       _main_outs->set_solo_level (_solo_level);
+       _main_outs->set_solo_isolated (_solo_isolated);
 }
 
 void
 Route::set_solo_isolated (bool yn, void *src)
 {
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_solo_isolated, yn, _mix_group);
+       if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
+               _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
                return;
        }
 
-       _main_outs->set_solo_isolated (yn);
-       solo_isolated_changed (src);
+       if (yn != _solo_isolated) {
+               _solo_isolated = yn;
+
+               /* tell main outs what the solo situation is
+                */
+               
+               _main_outs->set_solo_level (_solo_level);
+               _main_outs->set_solo_isolated (_solo_isolated);
+
+               solo_isolated_changed (src);
+       }
 }
 
 bool
 Route::solo_isolated () const 
 {
-       return _main_outs->solo_isolated();
+       return _solo_isolated;
 }
 
 void
 Route::set_mute (bool yn, void *src)
 {
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_mute, yn, _mix_group);
+       if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Mute)) {
+               _route_group->apply (&Route::set_mute, yn, _route_group);
                return;
        }
 
@@ -520,26 +595,19 @@ Route::muted() const
        return _mute_master->muted ();
 }
 
-#if DEFINE_IF_YOU_NEED_THIS
+#if 0
 static void
 dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
 {
        cerr << name << " {" << endl;
        for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
                        p != procs.end(); ++p) {
-               cerr << "\t" << (*p)->name() << endl;
+               cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl;
        }
        cerr << "}" << endl;
 }
 #endif
 
-Route::ProcessorList::iterator
-Route::prefader_iterator() 
-{
-       Glib::RWLock::ReaderLock lm (_processor_lock);
-       return find (_processors.begin(), _processors.end(), _amp);
-}
-
 int
 Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err)
 {
@@ -551,23 +619,10 @@ Route::add_processor (boost::shared_ptr<Processor> processor, Placement placemen
 
        if (placement == PreFader) {
                /* generic pre-fader: insert immediately before the amp */
-               loc = find(_processors.begin(), _processors.end(), _amp);
+               loc = find (_processors.begin(), _processors.end(), _amp);
        } else {
-               /* generic post-fader: insert at end */
-               loc = _processors.end();
-
-               if (processor->visible() && !_processors.empty()) {
-                       /* check for invisible processors stacked at the end and leave them there */
-                       ProcessorList::iterator p;
-                       p = _processors.end();
-                       --p;
-                       cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
-                       while (!(*p)->visible() && p != _processors.begin()) {
-                               --p;
-                       }
-                       ++p;
-                       loc = p;
-               }
+               /* generic post-fader: insert right before the main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
        }
 
        return add_processor (processor, loc, err);
@@ -626,6 +681,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                        --ploc;
                        _processors.erase(ploc);
                        configure_processors_unlocked (0); // it worked before we tried to add it ...
+                       cerr << "configure failed\n";
                        return -1;
                }
        
@@ -638,12 +694,6 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                        
                }
                
-               if (_meter) {
-                       // Ensure peak vector sizes before the plugin is activated
-                       ChanCount potential_max_streams = ChanCount::max (processor->input_streams(), processor->output_streams());
-                       _meter->configure_io (potential_max_streams, potential_max_streams);
-               }
-
                // XXX: do we want to emit the signal here ? change call order.
                processor->activate ();
                processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
@@ -656,146 +706,130 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
        return 0;
 }
 
-bool
-Route::add_processor_from_xml (const XMLNode& node, Placement placement)
-{
-       ProcessorList::iterator loc;
-       if (placement == PreFader) {
-               /* generic pre-fader: insert immediately before the amp */
-               loc = find(_processors.begin(), _processors.end(), _amp);
-       } else {
-               /* generic post-fader: insert at end */
-               loc = _processors.end();
-       }
-
-       return add_processor_from_xml (node, loc);
-}
-
 bool
 Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter)
 {
        const XMLProperty *prop;
 
-       // legacy sessions use a different node name for sends
-       if (node.name() == "Send") {
-       
-               try {
-                       boost::shared_ptr<Send> send (new Send (_session, _mute_master, node));
-                       add_processor (send, iter); 
-                       return true;
-               } 
-               
-               catch (failed_constructor &err) {
-                       error << _("Send construction failed") << endmsg;
-                       return false;
-               }
-               
-       } else if (node.name() == "Processor") {
+       if (node.name() != "Processor") {
+               return false;
+       }
                
-               try {
-                       if ((prop = node.property ("type")) != 0) {
-
-
-                               cerr << _name << " : got processor type " << prop->value() << endl;
-
-                               boost::shared_ptr<Processor> processor;
-                               bool have_insert = false;
+       try {
+               if ((prop = node.property ("type")) != 0) {
+                       
+                       boost::shared_ptr<Processor> processor;
 
-                               if (prop->value() == "ladspa" || prop->value() == "Ladspa" || 
-                                   prop->value() == "lv2" ||
-                                   prop->value() == "vst" ||
-                                   prop->value() == "audiounit") {
+                       if (prop->value() == "ladspa" || prop->value() == "Ladspa" || 
+                           prop->value() == "lv2" ||
+                           prop->value() == "vst" ||
+                           prop->value() == "audiounit") {
                                        
-                                       processor.reset (new PluginInsert(_session, node));
-                                       have_insert = true;
+                               processor.reset (new PluginInsert(_session, node));
                                        
-                               } else if (prop->value() == "port") {
+                       } else if (prop->value() == "port") {
 
-                                       processor.reset (new PortInsert (_session, _mute_master, node));
+                               processor.reset (new PortInsert (_session, _mute_master, node));
                                
-                               } else if (prop->value() == "send") {
+                       } else if (prop->value() == "send") {
 
-                                       processor.reset (new Send (_session, _mute_master, node));
-                                       have_insert = true;
-                               
-                               } else if (prop->value() == "meter") {
+                               processor.reset (new Send (_session, _mute_master, node));
+
+                       } else if (prop->value() == "meter") {
+
+                               if (_meter) {
+                                       if (_meter->set_state (node)) {
+                                               return false;
+                                       } else {
+                                               return true;
+                                       }
+                               }
 
-                                       processor = _meter;
-                                       processor->set_state (node);
+                               _meter.reset (new PeakMeter (_session, node));                                          
+                               processor = _meter;
                                
-                               } else if (prop->value() == "amp") {
+                       } else if (prop->value() == "amp") {
+
+                               /* amp always exists */
                                        
-                                       processor = _amp;
-                                       processor->set_state (node);
+                               processor = _amp;
+                               if (processor->set_state (node)) {
+                                       return false;
+                               } else {
+                                       /* never any reason to add it */
+                                       return true;
+                               }
                                        
-                               } else if (prop->value() == "listen" || prop->value() == "deliver") {
+                       } else if (prop->value() == "intsend") {
 
-                                       /* XXX need to generalize */
+                               processor.reset (new InternalSend (_session, _mute_master, node));
 
-                                       processor = _control_outs;
-                                       processor->set_state (node);
+                       } else if (prop->value() == "intreturn") {
                                        
-                               } else if (prop->value() == "main-outs") {
+                               if (_intreturn) {
+                                       if (_intreturn->set_state (node)) {
+                                               return false;
+                                       } else {
+                                               return true;
+                                       }
+                               }
+                               _intreturn.reset (new InternalReturn (_session, node));
+                               processor = _intreturn;
+
+                       } else if (prop->value() == "main-outs") {
                                        
-                                       processor = _main_outs;
-                                       processor->set_state (node);
+                               if (_main_outs) {
+                                       if (_main_outs->set_state (node)) {
+                                               return false;
+                                       } else {
+                                               return true;
+                                       }
+                               }
 
-                               } else {
+                               _main_outs.reset (new Delivery (_session, _output, _mute_master, node));
+                               processor = _main_outs;
 
-                                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
-                               }
+                       } else {
+                               error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+                               return false;
+                       }
                                
-                               if (iter == _processors.end() && processor->visible() && !_processors.empty()) {
-                                       /* check for invisible processors stacked at the end and leave them there */
-                                       ProcessorList::iterator p;
-                                       p = _processors.end();
+                       if (iter == _processors.end() && processor->visible() && !_processors.empty()) {
+                               /* check for invisible processors stacked at the end and leave them there */
+                               ProcessorList::iterator p;
+                               p = _processors.end();
+                               --p;
+                               while (!(*p)->visible() && p != _processors.begin()) {
                                        --p;
-                                       cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
-                                       while (!(*p)->visible() && p != _processors.begin()) {
-                                               --p;
-                                       }
-                                       ++p;
-                                       iter = p;
                                }
-
-                               return (add_processor (processor, iter) == 0);
-                               
-                       } else {
-                               error << _("Processor XML node has no type property") << endmsg;
+                               ++p;
+                               iter = p;
                        }
-               }
 
-               catch (failed_constructor &err) {
-                       warning << _("processor could not be created. Ignored.") << endmsg;
+                       return (add_processor (processor, iter) == 0);
+                               
+               } else {
+                       error << _("Processor XML node has no type property") << endmsg;
                        return false;
                }
        }
-       return false;
+
+       catch (failed_constructor &err) {
+               warning << _("processor could not be created. Ignored.") << endmsg;
+               return false;
+       }
 }
 
 int
-Route::add_processors (const ProcessorList& others, Placement placement, ProcessorStreams* err)
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
        ProcessorList::iterator loc;
-       if (placement == PreFader) {
-               /* generic pre-fader: insert immediately before the amp */
-               loc = find(_processors.begin(), _processors.end(), _amp);
-       } else {
-               /* generic post-fader: insert at end */
-               loc = _processors.end();
 
-               if (!_processors.empty()) {
-                       /* check for invisible processors stacked at the end and leave them there */
-                       ProcessorList::iterator p;
-                       p = _processors.end();
-                       --p;
-                       cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
-                       while (!(*p)->visible() && p != _processors.begin()) {
-                               --p;
-                       }
-                       ++p;
-                       loc = p;
-               }
+       if (before) {
+               loc = find(_processors.begin(), _processors.end(), before);
+       } else {
+               /* nothing specified - at end but before main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
        }
 
        return add_processors (others, loc, err);
@@ -841,14 +875,13 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                                pi->set_count (1);
                                
-                               ChanCount m = max(pi->input_streams(), pi->output_streams());
-                               if (m > potential_max_streams)
+                               ChanCount m = max (pi->input_streams(), pi->output_streams());
+
+                               if (m > potential_max_streams) {
                                        potential_max_streams = m;
+                               }
                        }
 
-                       // Ensure peak vector sizes before the plugin is activated
-                       _meter->configure_io (potential_max_streams, potential_max_streams);
-                       
                        _processors.insert (iter, *i);
                        
                        if (configure_processors_unlocked (err)) {
@@ -997,31 +1030,6 @@ Route::ab_plugins (bool forward)
 }
        
        
-/* Figure out the streams that will feed into PreFader */
-ChanCount
-Route::pre_fader_streams() const
-{
-       boost::shared_ptr<Processor> processor;
-
-       /* Find the last pre-fader redirect that isn't a send; sends don't affect the number
-        * of streams. */
-       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if ((*i) == _amp) {
-                       break;
-               }
-               if (boost::dynamic_pointer_cast<Send> (*i) == 0) {
-                       processor = *i;
-               }
-       }
-       
-       if (processor) {
-               return processor->output_streams();
-       } else {
-               return _input->n_ports ();
-       }
-}
-
-
 /** Remove processors with a given placement.
  * @param p Placement of processors to remove.
  */
@@ -1043,26 +1051,43 @@ Route::clear_processors (Placement p)
                Glib::RWLock::WriterLock lm (_processor_lock);
                ProcessorList new_list;
                ProcessorStreams err;
+               bool seen_amp = false;
 
-               ProcessorList::iterator amp_loc = find(_processors.begin(), _processors.end(), _amp);
-               if (p == PreFader) {
-                       // Get rid of PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
-                               (*i)->drop_references ();
-                       }
-                       // Keep the rest
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               new_list.push_back (*i);
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+                       if (*i == _amp) {
+                               seen_amp = true;
                        }
-               } else {
-                       // Keep PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
+
+                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+                               
+                               /* you can't remove these */
+
                                new_list.push_back (*i);
-                       }
-                       new_list.push_back (_amp);
-                       // Get rid of PostFader processors
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               (*i)->drop_references ();
+
+                       } else {
+                               if (seen_amp) {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       case PostFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       }
+
+                               } else {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       case PostFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       }
+                               }
                        }
                }
 
@@ -1168,6 +1193,99 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
        return 0;
 }
 
+int
+Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err)
+{
+       ProcessorList deleted;
+       ProcessorList as_we_were;
+
+       if (!_session.engine().connected()) {
+               return 1;
+       }
+
+       processor_max_streams.reset();
+
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList::iterator i;
+               boost::shared_ptr<Processor> processor;
+
+               as_we_were = _processors;
+
+               for (i = _processors.begin(); i != _processors.end(); ) {
+                       
+                       processor = *i;
+
+                       /* these can never be removed */
+
+                       if (processor == _amp || processor == _meter || processor == _main_outs) {
+                               ++i;
+                               continue;
+                       }
+                       
+                       /* see if its in the list of processors to delete */
+                       
+                       if (find (to_be_deleted.begin(), to_be_deleted.end(), processor) == to_be_deleted.end()) {
+                               ++i;
+                               continue;
+                       }
+
+                       /* stop IOProcessors that send to JACK ports
+                          from causing noise as a result of no longer being
+                          run.
+                       */
+                       
+                       boost::shared_ptr<IOProcessor> iop;
+                       
+                       if ((iop = boost::dynamic_pointer_cast<IOProcessor> (processor)) != 0) {
+                               iop->disconnect ();
+                       }
+
+                       deleted.push_back (processor);
+                       i = _processors.erase (i);
+               }
+
+               if (deleted.empty()) {
+                       /* none of those in the requested list were found */
+                       return 0;
+               }
+
+               _output->set_user_latency (0);
+
+               if (configure_processors_unlocked (err)) {
+                       /* get back to where we where */
+                       _processors = as_we_were;
+                       /* we know this will work, because it worked before :) */
+                       configure_processors_unlocked (0);
+                       return -1;
+               }
+
+               _have_internal_generator = false;
+
+               for (i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+                               if (pi->is_generator()) {
+                                       _have_internal_generator = true;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* now try to do what we need to so that those that were removed will be deleted */
+
+       for (ProcessorList::iterator i = deleted.begin(); i != deleted.end(); ++i) {
+               (*i)->drop_references ();
+       }
+
+       processors_changed (); /* EMIT SIGNAL */
+
+       return 0;
+}
+
+
 int
 Route::configure_processors (ProcessorStreams* err)
 {
@@ -1195,6 +1313,7 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        ChanCount out;
        list< pair<ChanCount,ChanCount> > configuration;
        uint32_t index = 0;
+       
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
                if ((*p)->can_support_io_configuration(in, out)) {
                        configuration.push_back(make_pair(in, out));
@@ -1213,7 +1332,6 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
                (*p)->configure_io(c->first, c->second);
-               (*p)->activate();
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
                out = c->second;
@@ -1221,6 +1339,7 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
 
        // Ensure route outputs match last processor's outputs
        if (out != _output->n_ports ()) {
+               cerr << "For " << _name << " out/last mismatch - out = " << out << " vs. " << _output->n_ports() << endl;
                _output->ensure_io (out, false, this);
        }
 
@@ -1283,8 +1402,33 @@ Route::all_processors_active (Placement p, bool state)
        _session.set_dirty ();
 }
 
+bool
+Route::processor_is_prefader (boost::shared_ptr<Processor> 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, Placement placement, ProcessorStreams* err)
+Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
 {
        /* "new_order" is an ordered list of processors to be positioned according to "placement".
           NOTE: all processors in "new_order" MUST be marked as visible. There maybe additional
@@ -1299,11 +1443,8 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                ProcessorList::const_iterator niter;
                ProcessorList as_it_was_before = _processors;
                ProcessorList as_it_will_be;
-               ProcessorList::iterator start, end;
-
-               placement_range (placement, start, end);
 
-               oiter = start;
+               oiter = _processors.begin();
                niter = new_order.begin(); 
 
                while (niter !=  new_order.end()) {
@@ -1315,7 +1456,7 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                           its been deleted. If its there, append it to the temp list.
                        */
 
-                       if (oiter == end) {
+                       if (oiter == _processors.end()) {
 
                                /* no more elements in the old list, so just stick the rest of 
                                   the new order onto the temp list.
@@ -1323,7 +1464,6 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
 
                                as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end());
                                while (niter != new_order.end()) {
-                                       (*niter)->set_placement (placement);
                                        ++niter;
                                }
                                break;
@@ -1333,7 +1473,6 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                                if (!(*oiter)->visible()) {
 
                                        as_it_will_be.push_back (*oiter);
-                                       (*oiter)->set_placement (placement);
 
                                } else {
 
@@ -1344,7 +1483,6 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                                        } else {
                                                /* ignore this one, and add the next item from the new order instead */
                                                as_it_will_be.push_back (*niter);
-                                               (*niter)->set_placement (placement);
                                                ++niter;
                                        }
                                }
@@ -1388,6 +1526,8 @@ Route::state(bool full_state)
        ProcessorList::iterator i;
        char buf[32];
 
+       id().print (buf, sizeof (buf));
+       node->add_property("id", buf);
        node->add_property ("name", _name);
        node->add_property("default-type", _default_type.to_string());
 
@@ -1400,11 +1540,8 @@ Route::state(bool full_state)
        node->add_property("denormal-protection", _denormal_protection?"yes":"no");
        node->add_property("meter-point", enum_2_string (_meter_point));
 
-       if (_edit_group) {
-               node->add_property("edit-group", _edit_group->name());
-       }
-       if (_mix_group) {
-               node->add_property("mix-group", _mix_group->name());
+       if (_route_group) {
+               node->add_property("route-group", _route_group->name());
        }
 
        string order_string;
@@ -1459,7 +1596,7 @@ Route::set_state (const XMLNode& node)
 }
 
 int
-Route::_set_state (const XMLNode& node, bool call_base)
+Route::_set_state (const XMLNode& node, bool /*call_base*/)
 {
 
        XMLNodeList nlist;
@@ -1477,12 +1614,53 @@ Route::_set_state (const XMLNode& node, bool call_base)
                Route::set_name (prop->value());
        } 
 
+       if ((prop = node.property ("id")) != 0) {
+               _id = prop->value ();
+       }
+
        if ((prop = node.property (X_("flags"))) != 0) {
                _flags = Flag (string_2_enum (prop->value(), _flags));
        } else {
                _flags = Flag (0);
        }
+
+       /* add all processors (except amp, which is always present) */
+
+       nlist = node.children();
+       XMLNode processor_state (X_("processor_state"));
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+               
+               child = *niter;
+
+               if (child->name() == IO::state_node_name) {
+                       if ((prop = child->property (X_("direction"))) == 0) {
+                               continue;
+                       }
+                       
+                       if (prop->value() == "Input") {
+                               _input->set_state (*child);
+                       } else if (prop->value() == "Output") {
+                               _output->set_state (*child);
+                       }
+               }
+                       
+               if (child->name() == X_("Processor")) {
+                       processor_state.add_child_copy (*child);
+               }
+       }
+
+       set_processor_state (processor_state);
        
+       if ((prop = node.property ("solo_level")) != 0) {
+               _solo_level = 0; // needed for mod_solo_level() to work
+               mod_solo_level (atoi (prop->value()));
+       }
+
+       if ((prop = node.property ("solo-isolated")) != 0) {
+               set_solo_isolated (prop->value() == "yes", this);
+       }
+
        if ((prop = node.property (X_("phase-invert"))) != 0) {
                set_phase_invert (prop->value()=="yes"?true:false);
        }
@@ -1509,12 +1687,12 @@ Route::_set_state (const XMLNode& node, bool call_base)
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
        }
        
-       if ((prop = node.property (X_("edit-group"))) != 0) {
-               RouteGroup* edit_group = _session.edit_group_by_name(prop->value());
-               if(edit_group == 0) {
-                       error << string_compose(_("Route %1: unknown edit group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
+       if ((prop = node.property (X_("route-group"))) != 0) {
+               RouteGroup* route_group = _session.route_group_by_name(prop->value());
+               if (route_group == 0) {
+                       error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
                } else {
-                       set_edit_group(edit_group, this);
+                       set_route_group (route_group, this);
                }
        }
 
@@ -1535,7 +1713,7 @@ Route::_set_state (const XMLNode& node, bool call_base)
                                        error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
                                              << endmsg;
                                } else {
-                                       set_order_key (remaining.substr (0, equal).c_str(), n);
+                                       set_order_key (remaining.substr (0, equal), n);
                                }
                        }
 
@@ -1549,32 +1727,6 @@ Route::_set_state (const XMLNode& node, bool call_base)
                }
        }
 
-       nlist = node.children();
-       XMLNode processor_state (X_("processor_state"));
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-               
-               child = *niter;
-
-               if (child->name() == IO::state_node_name) {
-                       if ((prop = child->property (X_("direction"))) == 0) {
-                               continue;
-                       }
-                       
-                       if (prop->value() == "Input") {
-                               _input->set_state (*child);
-                       } else if (prop->value() == "Output") {
-                               _output->set_state (*child);
-                       }
-               }
-                       
-               if (child->name() == X_("Processor")) {
-                       processor_state.add_child_copy (*child);
-               }
-       }
-
-       set_processor_state (processor_state);
-       
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
                child = *niter;
 
@@ -1608,15 +1760,6 @@ Route::_set_state (const XMLNode& node, bool call_base)
                }
        }
 
-       if ((prop = node.property (X_("mix-group"))) != 0) {
-               RouteGroup* mix_group = _session.mix_group_by_name(prop->value());
-               if (mix_group == 0) {
-                       error << string_compose(_("Route %1: unknown mix group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
-               }  else {
-                       set_mix_group(mix_group, this);
-               }
-       }
-
        return 0;
 }
 
@@ -1636,21 +1779,28 @@ Route::set_processor_state (const XMLNode& node)
 {
        const XMLNodeList &nlist = node.children();
        XMLNodeConstIterator niter;
-       bool has_meter_processor = false; // legacy sessions don't
        ProcessorList::iterator i, o;
 
-       cerr << _name << " _set_processor_states\n";
-
        // Iterate through existing processors, remove those which are not in the state list
+
        for (i = _processors.begin(); i != _processors.end(); ) {
+
+               /* leave amp alone, always */
+
+               if ((*i) == _amp) {
+                       ++i;
+                       continue;
+               }
+
                ProcessorList::iterator tmp = i;
                ++tmp;
 
                bool processorInStateList = false;
-       
+
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
                        XMLProperty* id_prop = (*niter)->property(X_("id"));
+
                        if (id_prop && (*i)->id() == id_prop->value()) {
                                processorInStateList = true;
                                break;
@@ -1666,22 +1816,20 @@ Route::set_processor_state (const XMLNode& node)
 
        // Iterate through state list and make sure all processors are on the track and in the correct order,
        // set the state of existing processors according to the new state on the same go
+
        i = _processors.begin();
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter, ++i) {
                
                XMLProperty* prop = (*niter)->property ("type");
 
-               if (prop && prop->value() == "meter")  {
-                       has_meter_processor = true;
-               }
-
                o = i;
 
-               if (prop->value() != "meter" && prop->value() != "amp" && prop->value() != "main-outs") {
-
-                       // Check whether the next processor in the list 
-                       
+               // Check whether the next processor in the list is the right one,
+               // except for "amp" which is always there and may not have the
+               // old ID since it is always created anew in every Route
+               
+               if (prop->value() != "amp") {
                        while (o != _processors.end()) {
                                XMLProperty* id_prop = (*niter)->property(X_("id"));
                                if (id_prop && (*o)->id() == id_prop->value()) {
@@ -1694,6 +1842,7 @@ Route::set_processor_state (const XMLNode& node)
 
                // If the processor (*niter) is not on the route,
                // create it and move it to the correct location
+
                if (o == _processors.end()) {
 
                        if (add_processor_from_xml (**niter, i)) {
@@ -1702,10 +1851,11 @@ Route::set_processor_state (const XMLNode& node)
                                cerr << "Error restoring route: unable to restore processor" << endl;
                        }
 
-               // Otherwise, the processor already exists; just
-               // ensure it is at the location provided in the XML state
                } else {
 
+                       // Otherwise, the processor already exists; just
+                       // ensure it is at the location provided in the XML state
+
                        if (i != o) {
                                boost::shared_ptr<Processor> tmp = (*o);
                                _processors.erase (o); // remove the old copy
@@ -1713,6 +1863,8 @@ Route::set_processor_state (const XMLNode& node)
                                --i; // move iterator to the correct processor
                        }
 
+                       // and make it (just) so
+
                        (*i)->set_state (**niter);
                }
        }
@@ -1721,10 +1873,6 @@ Route::set_processor_state (const XMLNode& node)
           the XML state represents a working signal route.
        */
 
-       if (!has_meter_processor) {
-               set_meter_point (_meter_point, NULL);
-       }
-
        processors_changed ();
 }
 
@@ -1766,33 +1914,48 @@ Route::silence (nframes_t nframes)
        }
 }      
 
-boost::shared_ptr<Delivery>
-Route::add_listener (boost::shared_ptr<IO> io, const string& listen_name)
+void
+Route::add_internal_return ()
 {
-       string name = _name;
-       name += '[';
-       name += listen_name;
-       name += ']';
-       
-       boost::shared_ptr<Delivery> listener (new Delivery (_session, _mute_master, name, Delivery::Listen)); 
-
-       /* As an IO, our control outs need as many IO outputs as we have outputs
-        *   (we track the changes in ::output_change_handler()).
-        * As a processor, the listener is an identity processor
-        *   (i.e. it does not modify its input buffers whatsoever)
-        */
+       if (!_intreturn) {
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
+       }
+}
 
-       if (listener->output()->ensure_io (n_outputs(), true, this)) {
-               return boost::shared_ptr<Delivery>();
+BufferSet*
+Route::get_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+       
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+               
+               if (d) {
+                       BufferSet* bs = d->get_buffers ();
+                       return bs;
+               }
        }
        
-       add_processor (listener, PostFader);
+       return 0;
+}
 
-       return listener;
+void
+Route::release_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+       
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+               
+               if (d) {
+                       return d->release_buffers ();
+               }
+       }
 }
 
 int
-Route::listen_via (boost::shared_ptr<IO> io, const string& listen_name)
+Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*active*/, bool aux)
 {
        vector<string> ports;
        vector<string>::const_iterator i;
@@ -1800,128 +1963,104 @@ Route::listen_via (boost::shared_ptr<IO> io, const string& listen_name)
        {
                Glib::RWLock::ReaderLock rm (_processor_lock);
                
-               for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
-                       boost::shared_ptr<const Delivery> d = boost::dynamic_pointer_cast<const Delivery>(*x);
+               for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
+
+                       boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+
+                       if (d && d->target_route() == route) {
+                               
+                               /* if the target is the control outs, then make sure
+                                  we take note of which i-send is doing that.
+                               */
+
+                               if (route == _session.control_out()) {
+                                       _control_outs = boost::dynamic_pointer_cast<Delivery>(d);
+                               }
 
-                       if (d && d->output() == io) {
                                /* already listening via the specified IO: do nothing */
+                               
                                return 0;
                        }
                }
        }
-
-       uint32_t ni = io->n_ports().n_total();
-
-       for (uint32_t n = 0; n < ni; ++n) {
-               ports.push_back (io->nth (n)->name());
-       }
-
-       if (ports.empty()) {
-               return 0;
-       }
        
-       boost::shared_ptr<Delivery> listen_point = add_listener (io, listen_name);
-       
-       /* XXX hack for now .... until we can generalize listen points */
+       boost::shared_ptr<InternalSend> listener;
 
-       _control_outs = listen_point;
+       try {
+               listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
 
-       /* now connect to the named ports */
-       
-       ni = listen_point->output()->n_ports().n_total();
-       size_t psize = ports.size();
+       } catch (failed_constructor& err) {
+               return -1;
+       }
 
-       for (size_t n = 0; n < ni; ++n) {
-               if (listen_point->output()->connect (listen_point->output()->nth (n), ports[n % psize], this)) {
-                       error << string_compose (_("could not connect %1 to %2"),
-                                                listen_point->output()->nth (n)->name(), ports[n % psize]) << endmsg;
-                       return -1;
-               }
+       if (route == _session.control_out()) {
+               _control_outs = listener;
        }
 
+       add_processor (listener, placement);
        
        return 0;
 }      
 
 void
-Route::drop_listen (boost::shared_ptr<IO> io)
+Route::drop_listen (boost::shared_ptr<Route> route)
 {
        ProcessorStreams err;
        ProcessorList::iterator tmp;
 
-       Glib::RWLock::ReaderLock rm (_processor_lock);
+       Glib::RWLock::ReaderLock rl(_processor_lock);
+       rl.acquire ();
        
+  again:
        for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) {
                
-               tmp = x;
-               ++tmp;
-               
-               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery>(*x);
+               boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
                
-               if (d && d->output() == io) {
-                       /* already listening via the specified IO: do nothing */
+               if (d && d->target_route() == route) {
+                       rl.release ();
                        remove_processor (*x, &err);
-                       
-               } 
-               
-               x = tmp;
-       }
-}
+                       rl.acquire ();
 
-void
-Route::set_edit_group (RouteGroup *eg, void *src)
+                        /* list could have been demolished while we dropped the lock
+                          so start over.
+                       */
 
-{
-       if (eg == _edit_group) {
-               return;
+                       goto again; 
+               } 
        }
 
-       if (_edit_group) {
-               _edit_group->remove (this);
-       }
+       rl.release ();
 
-       if ((_edit_group = eg) != 0) {
-               _edit_group->add (this);
+       if (route == _session.control_out()) {
+               _control_outs.reset ();
        }
-
-       _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
-}
-
-void
-Route::drop_edit_group (void *src)
-{
-       _edit_group = 0;
-       _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
-Route::set_mix_group (RouteGroup *mg, void *src)
-
+Route::set_route_group (RouteGroup *rg, void *src)
 {
-       if (mg == _mix_group) {
+       if (rg == _route_group) {
                return;
        }
 
-       if (_mix_group) {
-               _mix_group->remove (this);
+       if (_route_group) {
+               _route_group->remove (this);
        }
 
-       if ((_mix_group = mg) != 0) {
-               _mix_group->add (this);
+       if ((_route_group = rg) != 0) {
+               _route_group->add (this);
        }
 
        _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+       route_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
-Route::drop_mix_group (void *src)
+Route::drop_route_group (void *src)
 {
-       _mix_group = 0;
+       _route_group = 0;
        _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+       route_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
@@ -1933,30 +2072,35 @@ Route::set_comment (string cmt, void *src)
 }
 
 bool
-Route::feeds (boost::shared_ptr<IO> other)
+Route::feeds (boost::shared_ptr<Route> other)
 {
-       if (_output->connected_to (other)) {
+       // cerr << _name << endl;
+
+       if (_output->connected_to (other->input())) {
+               // cerr << "\tdirect FEEDS " << other->name() << endl;
                return true;
        }
 
-       /* check IOProcessors which may also interconnect Routes */
-
        for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) {
-
+               
                boost::shared_ptr<IOProcessor> iop;
                
                if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
-                       if (iop->output() && iop->output()->connected_to (other)) {
+                       if (iop->feeds (other)) {
+                               // cerr << "\tIOP " << iop->name() << " feeds " << other->name() << endl;
                                return true;
+                       } else {
+                               // cerr << "\tIOP " << iop->name() << " does NOT feeds " << other->name() << endl;
                        }
                }
        }
 
+       // cerr << "\tdoes NOT FEED " << other->name() << endl;
        return false;
 }
 
 void
-Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_flush_processors)
+Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
        nframes_t now = _session.transport_frame();
 
@@ -1982,7 +2126,7 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f
 }
 
 void
-Route::input_change_handler (IOChange change, void *src)
+Route::input_change_handler (IOChange change, void * /*src*/)
 {
        if ((change & ConfigurationChanged)) {
                configure_processors (0);
@@ -1990,7 +2134,7 @@ Route::input_change_handler (IOChange change, void *src)
 }
 
 void
-Route::output_change_handler (IOChange change, void *src)
+Route::output_change_handler (IOChange change, void * /*src*/)
 {
        if ((change & ConfigurationChanged)) {
                
@@ -2012,7 +2156,7 @@ Route::pans_required () const
 
 int 
 Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,  
-               bool session_state_changing, bool can_record, bool rec_monitors_input)
+               bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
 {
        if (n_outputs().n_total() == 0) {
                return 0;
@@ -2058,7 +2202,7 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
 
 int
 Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int declick,
-            bool can_record, bool rec_monitors_input)
+            bool /*can_record*/, bool /*rec_monitors_input*/)
 {
        {
                // automation snapshot can also be called from the non-rt context
@@ -2093,8 +2237,8 @@ Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int
 }
 
 int
-Route::silent_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame
-                   bool can_record, bool rec_monitors_input)
+Route::silent_roll (nframes_t nframes, sframes_t /*start_frame*/, sframes_t /*end_frame*/
+                   bool /*can_record*/, bool /*rec_monitors_input*/)
 {
        silence (nframes);
        return 0;
@@ -2178,6 +2322,36 @@ Route::set_meter_point (MeterPoint p, void *src)
                _session.set_dirty ();
        }
 }
+void
+Route::put_control_outs_at (Placement p)
+{
+       if (!_control_outs) {
+               return;
+       }
+
+       // Move meter in the processors list
+       ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs);
+       _processors.erase(loc);
+
+       switch (p) {
+       case PreFader:
+               loc = find(_processors.begin(), _processors.end(), _amp);
+               if (loc != _processors.begin()) {
+                       --loc;
+               }
+               break;
+       case PostFader:
+               loc = find(_processors.begin(), _processors.end(), _amp);
+               assert (loc != _processors.end());
+               loc++;
+               break;
+       }
+
+       _processors.insert(loc, _control_outs);
+
+       processors_changed (); /* EMIT SIGNAL */
+       _session.set_dirty ();
+}
 
 nframes_t
 Route::update_total_latency ()
@@ -2320,7 +2494,7 @@ Route::set_pending_declick (int declick)
  */
 
 void
-Route::shift (nframes64_t pos, nframes64_t frames)
+Route::shift (nframes64_t /*pos*/, nframes64_t /*frames*/)
 {
 #ifdef THIS_NEEDS_FIXING_FOR_V3
 
@@ -2379,10 +2553,12 @@ Route::set_name (const string& str)
 {
        bool ret;
        string ioproc_name;
+       string name;
+
+       name = Route::ensure_track_or_route_name (str, _session);
+       SessionObject::set_name (name);
        
-       SessionObject::set_name (str);
-       
-       ret = (_input->set_name(str) && _output->set_name(str));
+       ret = (_input->set_name(name) && _output->set_name(name));
 
        if (ret) {
                
@@ -2390,17 +2566,12 @@ Route::set_name (const string& str)
                
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        
-                       /* rename all processors with outputs to reflect our new name */
+                       /* rename all I/O processors that have inputs or outputs */
 
                        boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
 
-                       if (iop) {
-                               string iop_name = str;
-                               iop_name += '[';
-                               iop_name += "XXX FIX ME XXX";
-                               iop_name += ']';
-                               
-                               if (!iop->set_name (iop_name)) {
+                       if (iop && (iop->output() || iop->input())) {
+                               if (!iop->set_name (name)) {
                                        ret = false;
                                }
                        }
@@ -2412,15 +2583,15 @@ Route::set_name (const string& str)
 }
 
 boost::shared_ptr<Send>
-Route::send_for (boost::shared_ptr<const IO> target) const
+Route::internal_send_for (boost::shared_ptr<const Route> target) const
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
 
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               boost::shared_ptr<Send> send;
+               boost::shared_ptr<InternalSend> send;
                
-               if ((send = boost::dynamic_pointer_cast<Send>(*i)) != 0) {
-                       if (send->output()->connected_to (target)) {
+               if ((send = boost::dynamic_pointer_cast<InternalSend>(*i)) != 0) {
+                       if (send->target_route() == target) {
                                return send;
                        }
                }
@@ -2474,7 +2645,22 @@ void
 Route::meter ()
 {
        Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+
+       assert (_meter);
+
        _meter->meter ();
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               boost::shared_ptr<Send> s;
+               boost::shared_ptr<Return> r;
+
+               if ((s = boost::dynamic_pointer_cast<Send> (*i)) != 0) {
+                       s->meter()->meter();
+               } else if ((r = boost::dynamic_pointer_cast<Return> (*i)) != 0) {
+                       r->meter()->meter ();
+               }
+       }
 }
 
 boost::shared_ptr<Panner>