Don't strip suffixes twice from peakfile names, otherwise source names like fred...
[ardour.git] / libs / ardour / route.cc
index 1dfa66a7b1c0d213e31da34b6d3e7b975b39bbb0..880db63c7dec87d2503d3715494ef5414989ebd1 100644 (file)
@@ -58,6 +58,7 @@
 #include "ardour/session.h"
 #include "ardour/timestamps.h"
 #include "ardour/utils.h"
+#include "ardour/graph.h"
 
 #include "i18n.h"
 
@@ -71,14 +72,14 @@ PBD::Signal0<void> Route::RemoteControlIDChange;
 
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
-       , AutomatableControls (sess)
+       , Automatable (sess)
+       , GraphNode( sess.route_graph )
         , _active (true)
         , _initial_delay (0)
         , _roll_delay (0)
        , _flags (flg)
         , _pending_declick (true)
         , _meter_point (MeterPostFader)
-        , _phase_invert (0)
         , _self_solo (false)
         , _soloed_by_others_upstream (0)
         , _soloed_by_others_downstream (0)
@@ -107,7 +108,7 @@ Route::init ()
 
        _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
        _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
-       
+
        add_control (_solo_control);
        add_control (_mute_control);
 
@@ -211,7 +212,7 @@ Route::remote_control_id() const
        return _remote_control_id;
 }
 
-long
+int32_t
 Route::order_key (std::string const & name) const
 {
        OrderKeys::const_iterator i = order_keys.find (name);
@@ -223,17 +224,34 @@ Route::order_key (std::string const & name) const
 }
 
 void
-Route::set_order_key (std::string const & name, long n)
+Route::set_order_key (std::string const & name, int32_t n)
 {
-       order_keys[name] = n;
+       bool changed = false;
+
+       /* This method looks more complicated than it should, but
+          it's important that we don't emit order_key_changed unless
+          it actually has, as expensive things happen on receipt of that
+          signal.
+       */
+
+       if (order_keys.find(name) == order_keys.end() || order_keys[name] != n) {
+               order_keys[name] = n;
+               changed = true;
+       }
 
        if (Config->get_sync_all_route_ordering()) {
                for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) {
-                       x->second = n;
+                       if (x->second != n) {
+                               x->second = n;
+                               changed = true;
+                       }
                }
        }
 
-       _session.set_dirty ();
+       if (changed) {
+               order_key_changed (); /* EMIT SIGNAL */
+               _session.set_dirty ();
+       }
 }
 
 /** Set all order keys to be the same as that for `base', if such a key
@@ -248,7 +266,7 @@ Route::sync_order_keys (std::string const & base)
        }
 
        OrderKeys::iterator i;
-       uint32_t key;
+       int32_t key;
 
        if ((i = order_keys.find (base)) == order_keys.end()) {
                /* key doesn't exist, use the first existing key (during session initialization) */
@@ -261,8 +279,17 @@ Route::sync_order_keys (std::string const & base)
                i = order_keys.begin();
        }
 
+       bool changed = false;
+       
        for (; i != order_keys.end(); ++i) {
-               i->second = key;
+               if (i->second != key) {
+                       i->second = key;
+                       changed = true;
+               }
+       }
+
+       if (changed) {
+               order_key_changed (); /* EMIT SIGNAL */
        }
 }
 
@@ -352,7 +379,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,
+                              framepos_t start_frame, framepos_t end_frame, nframes_t nframes,
                               bool /*with_processors*/, int declick)
 {
        bool monitor;
@@ -385,9 +412,9 @@ Route::process_output_buffers (BufferSet& bufs,
           ----------------------------------------------------------------------------------------- */
 
        if (declick > 0) {
-               Amp::apply_gain (bufs, nframes, 0.0, 1.0);
+               Amp::declick (bufs, nframes, 1);
        } else if (declick < 0) {
-               Amp::apply_gain (bufs, nframes, 1.0, 0.0);
+               Amp::declick (bufs, nframes, -1);
        }
 
        _pending_declick = 0;
@@ -396,7 +423,7 @@ Route::process_output_buffers (BufferSet& bufs,
           DENORMAL CONTROL/PHASE INVERT
           ----------------------------------------------------------------------------------------- */
 
-       if (_phase_invert) {
+       if (_phase_invert.any ()) {
 
                int chn = 0;
 
@@ -405,7 +432,7 @@ Route::process_output_buffers (BufferSet& bufs,
                        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
                                Sample* const sp = i->data();
 
-                               if (_phase_invert & chn) {
+                               if (_phase_invert[chn]) {
                                        for (nframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx]  = -sp[nx];
                                                sp[nx] += 1.0e-27f;
@@ -422,7 +449,7 @@ Route::process_output_buffers (BufferSet& bufs,
                        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
                                Sample* const sp = i->data();
 
-                               if (_phase_invert & (1<<chn)) {
+                               if (_phase_invert[chn]) {
                                        for (nframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] = -sp[nx];
                                        }
@@ -448,25 +475,21 @@ Route::process_output_buffers (BufferSet& bufs,
           and go ....
           ----------------------------------------------------------------------------------------- */
 
-       Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
-
-       if (rm.locked()) {
-                //cerr << name() << " upstream solo " << _soloed_by_others_upstream
-                // << " downstream solo " << _soloed_by_others_downstream
-                // << " self " << _self_solo
-                //<< endl;
-               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
-                       if (bufs.count() != (*i)->input_streams()) {
-                               cerr << _name << " bufs = " << bufs.count()
-                                    << " input for " << (*i)->name() << " = " << (*i)->input_streams()
-                                    << endl;
-                       }
-                       assert (bufs.count() == (*i)->input_streams());
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
-                       (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
-                       bufs.set_count ((*i)->output_streams());
+               if (bufs.count() != (*i)->input_streams()) {
+                       cerr << _name << " bufs = " << bufs.count()
+                            << " input for " << (*i)->name() << " = " << (*i)->input_streams()
+                            << endl;
                }
+               assert (bufs.count() == (*i)->input_streams());
+                
+                /* should we NOT run plugins here if the route is inactive?
+                   do we catch route != active somewhere higher?
+                */
+
+               (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
+               bufs.set_count ((*i)->output_streams());
        }
 }
 
@@ -477,7 +500,7 @@ Route::n_process_buffers ()
 }
 
 void
-Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+Route::passthru (framepos_t start_frame, framepos_t end_frame, nframes_t nframes, int declick)
 {
        BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
 
@@ -486,7 +509,7 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
        assert (bufs.available() >= input_streams());
 
        if (_input->n_ports() == ChanCount::ZERO) {
-               silence (nframes);
+               silence_unlocked (nframes);
        }
 
        bufs.set_count (input_streams());
@@ -517,7 +540,7 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
 }
 
 void
-Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, nframes_t nframes, int declick)
 {
        BufferSet& bufs (_session.get_silent_buffers (n_process_buffers()));
        bufs.set_count (_input->n_ports());
@@ -895,13 +918,32 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
 }
 
 bool
-Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter)
+Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 {
        const XMLProperty *prop;
 
        try {
                boost::shared_ptr<Processor> processor;
 
+               /* bit of a hack: get the `placement' property from the <Redirect> tag here
+                  so that we can add the processor in the right place (pre/post-fader)
+               */
+
+               XMLNodeList const & children = node.children ();
+               XMLNodeList::const_iterator i = children.begin ();
+               
+               while (i != children.end() && (*i)->name() != X_("Redirect")) {
+                       ++i;
+               }
+
+               Placement placement = PreFader;
+
+               if (i != children.end()) {
+                       if ((prop = (*i)->property (X_("placement"))) != 0) {
+                               placement = Placement (string_2_enum (prop->value(), placement));
+                       }
+               }
+
                if (node.name() == "Insert") {
 
                        if ((prop = node.property ("type")) != 0) {
@@ -934,19 +976,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis
                         return false;
                 }
 
-               if (iter == _processors.end() && processor->display_to_user() && !_processors.empty()) {
-                       /* check for invisible processors stacked at the end and leave them there */
-                       ProcessorList::iterator p;
-                       p = _processors.end();
-                       --p;
-                       while (!(*p)->display_to_user() && p != _processors.begin()) {
-                               --p;
-                       }
-                       ++p;
-                       iter = p;
-               }
-
-               return (add_processor (processor, iter) == 0);
+               return (add_processor (processor, placement) == 0);
        }
 
        catch (failed_constructor &err) {
@@ -1460,18 +1490,16 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        uint32_t index = 0;
 
        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name));
-#ifndef NDEBUG
        DEBUG_TRACE (DEBUG::Processors, "{\n");
        for (list<boost::shared_ptr<Processor> >::const_iterator p = _processors.begin(); p != _processors.end(); ++p) {
                DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID = %2\n", (*p)->name(), (*p)->id()));
        }
        DEBUG_TRACE (DEBUG::Processors, "}\n");
-#endif
 
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
 
                if ((*p)->can_support_io_configuration(in, out)) {
-                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1in = %2 out = %3\n",(*p)->name(), in, out));
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 in = %2 out = %3\n",(*p)->name(), in, out));
                        configuration.push_back(make_pair(in, out));
                        in = out;
                } else {
@@ -1504,6 +1532,8 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                _session.ensure_buffers (n_process_buffers ());
        }
 
+       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name));
+
        _in_configure_processors = false;
        return 0;
 }
@@ -1700,7 +1730,9 @@ Route::state(bool full_state)
        }
 
        node->add_property("active", _active?"yes":"no");
-       node->add_property("phase-invert", _phase_invert?"yes":"no");
+       string p;
+       boost::to_string (_phase_invert, p);
+       node->add_property("phase-invert", p);
        node->add_property("denormal-protection", _denormal_protection?"yes":"no");
        node->add_property("meter-point", enum_2_string (_meter_point));
 
@@ -1847,7 +1879,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
        }
 
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (string_is_affirmative (prop->value()));
+               set_phase_invert (boost::dynamic_bitset<> (prop->value ()));
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
@@ -1870,7 +1902,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
 
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
-               long n;
+               int32_t n;
 
                string::size_type colon, equal;
                string remaining = prop->value();
@@ -1881,7 +1913,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                                error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
                                      << endmsg;
                        } else {
-                               if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) {
+                               if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
                                        error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
                                              << endmsg;
                                } else {
@@ -1962,7 +1994,11 @@ Route::_set_state_2X (const XMLNode& node, int version)
        }
        
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (string_is_affirmative (prop->value()));
+               boost::dynamic_bitset<> p (_input->n_ports().n_audio ());
+               if (string_is_affirmative (prop->value ())) {
+                       p.set ();
+               }                       
+               set_phase_invert (p);
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
@@ -2046,7 +2082,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
 
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
-               long n;
+               int32_t n;
 
                string::size_type colon, equal;
                string remaining = prop->value();
@@ -2057,7 +2093,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
                                error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
                                        << endmsg;
                        } else {
-                               if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) {
+                               if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
                                        error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
                                                << endmsg;
                                } else {
@@ -2075,23 +2111,6 @@ Route::_set_state_2X (const XMLNode& node, int version)
                }
        }
 
-       /* add standard processors */
-
-       //_meter.reset (new PeakMeter (_session));
-       //add_processor (_meter, PreFader);
-
-       if (is_monitor()) {
-               /* where we listen to tracks */
-               _intreturn.reset (new InternalReturn (_session));
-               add_processor (_intreturn, PreFader);
-
-                _monitor_control.reset (new MonitorProcessor (_session));
-                add_processor (_monitor_control, PostFader);
-       }
-
-       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
-       add_processor (_main_outs, PostFader);
-
        /* IOs */
 
        nlist = node.children ();
@@ -2142,6 +2161,9 @@ Route::_set_state_2X (const XMLNode& node, int version)
                                
                                if (io_child->name() == X_("Panner")) {
                                        _main_outs->panner()->set_state(*io_child, version);
+                               } else if (io_child->name() == X_("Automation")) {
+                                       /* IO's automation is for the fader */
+                                       _amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation));
                                }
                        }
                }
@@ -2171,7 +2193,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
                        XMLNode *cmt = *(child->children().begin());
                        _comment = cmt->content();
 
-               } else if (child->name() == X_("Extra")) {
+               } else if (child->name() == X_("extra")) {
 
                        _extra_xml = new XMLNode (*child);
 
@@ -2216,7 +2238,7 @@ Route::set_processor_state_2X (XMLNodeList const & nList, int version)
        */
 
        for (XMLNodeConstIterator i = nList.begin(); i != nList.end(); ++i) {
-               add_processor_from_xml_2X (**i, version, _processors.begin ());
+               add_processor_from_xml_2X (**i, version);
        }
 }
 
@@ -2326,31 +2348,37 @@ Route::curve_reallocate ()
 void
 Route::silence (nframes_t nframes)
 {
-       if (!_silent) {
-
-               _output->silence (nframes);
-
-               {
-                       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return;
+       }
 
-                       if (lm.locked()) {
-                               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                                       boost::shared_ptr<PluginInsert> pi;
+       silence_unlocked (nframes);
+}
 
-                                       if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
-                                               // skip plugins, they don't need anything when we're not active
-                                               continue;
-                                       }
+void
+Route::silence_unlocked (nframes_t nframes)
+{
+       /* Must be called with the processor lock held */
+       
+       if (!_silent) {
 
-                                       (*i)->silence (nframes);
-                               }
+               _output->silence (nframes);
 
-                               if (nframes == _session.get_block_size()) {
-                                       // _silent = true;
-                               }
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       
+                       if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
+                               // skip plugins, they don't need anything when we're not active
+                               continue;
                        }
+                       
+                       (*i)->silence (nframes);
+               }
+               
+               if (nframes == _session.get_block_size()) {
+                       // _silent = true;
                }
-
        }
 }
 
@@ -2590,7 +2618,7 @@ Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
 void
 Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
-       nframes_t now = _session.transport_frame();
+       framepos_t now = _session.transport_frame();
 
        {
                Glib::RWLock::ReaderLock lm (_processor_lock);
@@ -2599,11 +2627,12 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c
                        automation_snapshot (now, true);
                }
 
+                Automatable::transport_stopped (now);
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
                        if (Config->get_plugins_stop_with_transport() && can_flush_processors) {
-                               (*i)->deactivate ();
-                               (*i)->activate ();
+                                (*i)->flush ();
                        }
 
                        (*i)->transport_stopped (now);
@@ -2616,18 +2645,47 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c
 void
 Route::input_change_handler (IOChange change, void * /*src*/)
 {
-       if ((change & ConfigurationChanged)) {
+       if ((change.type & IOChange::ConfigurationChanged)) {
                configure_processors (0);
+               _phase_invert.resize (_input->n_ports().n_audio ());
+               io_changed (); /* EMIT SIGNAL */
        }
 }
 
 void
 Route::output_change_handler (IOChange change, void * /*src*/)
 {
-       if ((change & ConfigurationChanged)) {
+       if ((change.type & IOChange::ConfigurationChanged)) {
 
                /* XXX resize all listeners to match _main_outs? */
 
+               /* Auto-connect newly-created outputs, unless we're auto-connecting to master
+                  and we are master (as an auto-connect in this situation would cause a
+                  feedback loop)
+               */
+               AutoConnectOption ac = Config->get_output_auto_connect ();
+               if (ac == AutoConnectPhysical || (ac == AutoConnectMaster && !is_master ())) {
+
+                       ChanCount start = change.before;
+                       
+                       for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
+                               if (change.before.get(*i) < change.after.get(*i)) {
+                                       /* the existing ChanCounts don't matter for this call as they are only
+                                          to do with matching input and output indices, and we are only changing
+                                          outputs here.
+                                       */
+                                       ChanCount dummy;
+
+                                       /* only auto-connect the newly-created outputs, not the ones that were
+                                          already there
+                                       */
+                                       start.set (*i, start.get (*i) + 1);
+                                       
+                                       _session.auto_connect_route (this, dummy, dummy, false, ChanCount(), change.before);
+                               }
+                       }
+               }
+
                // configure_processors (0);
        }
 }
@@ -2643,15 +2701,20 @@ Route::pans_required () const
 }
 
 int
-Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
+Route::no_roll (nframes_t nframes, framepos_t start_frame, framepos_t end_frame,
                bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
 {
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return 0;
+       }
+
        if (n_outputs().n_total() == 0) {
                return 0;
        }
 
        if (!_active || n_inputs() == ChanCount::ZERO)  {
-               silence (nframes);
+               silence_unlocked (nframes);
                return 0;
        }
        if (session_state_changing) {
@@ -2661,7 +2724,7 @@ Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
                           
                           XXX note the absurdity of ::no_roll() being called when we ARE rolling!
                        */
-                       silence (nframes);
+                       silence_unlocked (nframes);
                        return 0;
                }
                /* we're really not rolling, so we're either delivery silence or actually
@@ -2681,14 +2744,14 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
        if (_roll_delay > nframes) {
 
                _roll_delay -= nframes;
-               silence (nframes);
+               silence_unlocked (nframes);
                /* transport frame is not legal for caller to use */
                return 0;
 
        } else if (_roll_delay > 0) {
 
                nframes -= _roll_delay;
-               silence (_roll_delay);
+               silence_unlocked (_roll_delay);
                /* we've written _roll_delay of samples into the
                   output ports, so make a note of that for
                   future reference.
@@ -2703,25 +2766,22 @@ 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,
+Route::roll (nframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick,
             bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
-       {
-               // automation snapshot can also be called from the non-rt context
-               // and it uses the processor list, so we try to acquire the lock here
-               Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
-
-               if (lm.locked()) {
-                       automation_snapshot (_session.transport_frame(), false);
-               }
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return 0;
        }
+       
+       automation_snapshot (_session.transport_frame(), false);
 
        if (n_outputs().n_total() == 0) {
                return 0;
        }
 
        if (!_active || n_inputs().n_total() == 0) {
-               silence (nframes);
+               silence_unlocked (nframes);
                return 0;
        }
 
@@ -2739,7 +2799,7 @@ 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*/,
+Route::silent_roll (nframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
                    bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
        silence (nframes);
@@ -2792,8 +2852,7 @@ Route::flush_processors ()
        Glib::RWLock::ReaderLock lm (_processor_lock);
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               (*i)->deactivate ();
-               (*i)->activate ();
+                (*i)->flush ();
        }
 }
 
@@ -2990,7 +3049,7 @@ Route::SoloControllable::SoloControllable (std::string name, Route& r)
 }
 
 void
-Route::SoloControllable::set_value (float val)
+Route::SoloControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
 # if 0
@@ -3009,7 +3068,7 @@ Route::SoloControllable::set_value (float val)
 #endif
 }
 
-float
+double
 Route::SoloControllable::get_value (void) const
 {
        if (Config->get_solo_control_is_listen_control()) {
@@ -3029,7 +3088,7 @@ Route::MuteControllable::MuteControllable (std::string name, Route& r)
 }
 
 void
-Route::MuteControllable::set_value (float val)
+Route::MuteControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
 # if 0
@@ -3043,7 +3102,7 @@ Route::MuteControllable::set_value (float val)
 #endif
 }
 
-float
+double
 Route::MuteControllable::get_value (void) const
 {
        return route.muted() ? 1.0f : 0.0f;
@@ -3089,7 +3148,7 @@ Route::set_pending_declick (int declick)
  */
 
 void
-Route::shift (nframes64_t /*pos*/, nframes64_t /*frames*/)
+Route::shift (framepos_t /*pos*/, framecnt_t /*frames*/)
 {
 #ifdef THIS_NEEDS_FIXING_FOR_V3
 
@@ -3195,25 +3254,39 @@ Route::internal_send_for (boost::shared_ptr<const Route> target) const
        return boost::shared_ptr<Send>();
 }
 
+/** @param c Audio channel index.
+ *  @param yn true to invert phase, otherwise false.
+ */
 void
-Route::set_phase_invert (bool yn)
+Route::set_phase_invert (uint32_t c, bool yn)
 {
-       if (_phase_invert != yn) {
-                if (yn) {
-                        _phase_invert = 0xffff; // XXX all channels
-                } else {
-                        _phase_invert = 0; // XXX no channels
-                }
+       if (_phase_invert[c] != yn) {
+               _phase_invert[c] = yn;
+               phase_invert_changed (); /* EMIT SIGNAL */
+                _session.set_dirty ();
+       }
+}
 
+void
+Route::set_phase_invert (boost::dynamic_bitset<> p)
+{
+       if (_phase_invert != p) {
+               _phase_invert = p;
                phase_invert_changed (); /* EMIT SIGNAL */
                 _session.set_dirty ();
        }
 }
 
 bool
+Route::phase_invert (uint32_t c) const
+{
+       return _phase_invert[c];
+}
+
+boost::dynamic_bitset<>
 Route::phase_invert () const
 {
-       return _phase_invert != 0;
+       return _phase_invert;
 }
 
 void
@@ -3377,3 +3450,4 @@ Route::set_processor_positions ()
                }
        }
 }
+