change exposed type of various objects' gain controls; remove Amp::gain() as a shortcut
[ardour.git] / libs / ardour / route.cc
index eb98a2388f367fdc61f29e18daaf3363420c51e2..75f58b528a7cd50f378d8df966c7ecf36a46686c 100644 (file)
@@ -46,6 +46,7 @@
 #include "ardour/capturing_processor.h"
 #include "ardour/debug.h"
 #include "ardour/delivery.h"
+#include "ardour/gain_control.h"
 #include "ardour/internal_return.h"
 #include "ardour/internal_send.h"
 #include "ardour/meter.h"
@@ -135,12 +136,15 @@ Route::init ()
 
        _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
        _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
+       _phase_control.reset (new PhaseControllable (X_("phase"), shared_from_this ()));
 
        _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
        _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
+       _phase_control->set_flags (Controllable::Flag (_phase_control->flags() | Controllable::Toggle));
 
        add_control (_solo_control);
        add_control (_mute_control);
+       add_control (_phase_control);
 
        /* panning */
 
@@ -168,11 +172,22 @@ Route::init ()
 
        /* add amp processor  */
 
-       _amp.reset (new Amp (_session));
+       _gain_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, GainAutomation, shared_from_this ()));
+       add_control (_gain_control);
+
+       _amp.reset (new Amp (_session, X_("Fader"), _gain_control, true));
        add_processor (_amp, PostFader);
 
+       if (is_monitor ()) {
+               _amp->set_display_name (_("Monitor"));
+       }
+
        /* and input trim */
-       _trim.reset (new Amp (_session, "trim"));
+
+       _trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ()));
+       add_control (_trim_control);
+
+       _trim.reset (new Amp (_session, X_("Trim"), _trim_control, false));
        _trim->set_display_to_user (false);
 
        if (dynamic_cast<AudioTrack*>(this)) {
@@ -372,19 +387,30 @@ Route::ensure_track_or_route_name(string name, Session &session)
 }
 
 void
-Route::inc_gain (gain_t fraction, void *src)
+Route::inc_gain (gain_t factor)
 {
-       _amp->inc_gain (fraction, src);
+       /* To be used ONLY when doing group-relative gain adjustment, from
+        * ::set_gain()
+        */
+
+       float desired_gain = _gain_control->user_double();
+
+       if (fabsf (desired_gain) < GAIN_COEFF_SMALL) {
+               // really?! what's the idea here?
+               _gain_control->route_set_value (0.000001f + (0.000001f * factor));
+       } else {
+               _gain_control->route_set_value (desired_gain + (desired_gain * factor));
+       }
 }
 
 void
-Route::set_gain (gain_t val, void *src)
+Route::set_gain (gain_t val, Controllable::GroupControlDisposition group_override)
 {
-       if (src != 0 && _route_group && src != _route_group && _route_group->is_active() && _route_group->is_gain()) {
+       if (use_group (group_override, &RouteGroup::is_gain)) {
 
                if (_route_group->is_relative()) {
 
-                       gain_t usable_gain = _amp->gain();
+                       gain_t usable_gain = _gain_control->get_value();
                        if (usable_gain < 0.000001f) {
                                usable_gain = 0.000001f;
                        }
@@ -415,34 +441,28 @@ Route::set_gain (gain_t val, void *src)
                                }
                        }
 
-                       _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor, _route_group));
+                       _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor));
 
                } else {
 
-                       _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, _route_group));
+                       _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, Controllable::NoGroup));
                }
 
                return;
        }
 
-       if (val == _amp->gain()) {
+       if (val == _gain_control->get_value()) {
                return;
        }
 
-       _amp->set_gain (val, src);
-}
-
-void
-Route::inc_trim (gain_t fraction, void *src)
-{
-       _trim->inc_gain (fraction, src);
+       _gain_control->route_set_value (val);
 }
 
 void
-Route::set_trim (gain_t val, void * /* src */)
+Route::set_trim (gain_t val, Controllable::GroupControlDisposition /* group override */)
 {
        // TODO route group, see set_gain()
-       _trim->set_gain (val, 0);
+       _trim_control->route_set_value (val);
 }
 
 void
@@ -764,19 +784,14 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t
 }
 
 void
-Route::set_listen (bool yn, void* src, bool group_override)
+Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override)
 {
        if (_solo_safe) {
                return;
        }
 
-       bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
-       if (group_override && _route_group) {
-               group_active = !group_active;
-       }
-
-       if (_route_group && src != _route_group && group_active) {
-               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group, group_override));
+       if (use_group (group_override, &RouteGroup::is_solo)) {
+               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::NoGroup));
                return;
        }
 
@@ -791,7 +806,7 @@ Route::set_listen (bool yn, void* src, bool group_override)
                        }
                        _mute_master->set_soloed_by_others (false);
 
-                       listen_changed (src, group_active); /* EMIT SIGNAL */
+                       listen_changed (group_override); /* EMIT SIGNAL */
                }
        }
 }
@@ -807,11 +822,11 @@ Route::listening_via_monitor () const
 }
 
 void
-Route::set_solo_safe (bool yn, void *src)
+Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition /* group_override */)
 {
        if (_solo_safe != yn) {
                _solo_safe = yn;
-               solo_safe_changed (src);
+               solo_safe_changed ();
        }
 }
 
@@ -848,17 +863,17 @@ Route::clear_all_solo_state ()
 
        {
                PBD::Unwinder<bool> uw (_solo_safe, false);
-               set_solo (false, this);
+               set_solo (false, Controllable::NoGroup);
        }
 
        if (emit_changed) {
                set_mute_master_solo ();
-               solo_changed (false, this, false); /* EMIT SIGNAL */
+               solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
        }
 }
 
 void
-Route::set_solo (bool yn, void *src, bool group_override)
+Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override)
 {
        if (_solo_safe) {
                DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
@@ -870,23 +885,17 @@ Route::set_solo (bool yn, void *src, bool group_override)
                return;
        }
 
-       // explicit XOR
-       bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
-       if (group_override && _route_group) {
-               group_active = !group_active;
-       }
-       if (_route_group && src != _route_group && group_active) {
-               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group, group_override));
+       if (use_group (group_override, &RouteGroup::is_solo)) {
+               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::NoGroup));
                return;
        }
 
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n",
-                                                 name(), yn, src, (src == _route_group), self_soloed()));
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n",
+                                                 name(), yn, enum_2_string(group_override), self_soloed()));
 
        if (self_soloed() != yn) {
                set_self_solo (yn);
-               set_mute_master_solo ();
-               solo_changed (true, src, group_active); /* EMIT SIGNAL */
+               solo_changed (true, group_override); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
 
@@ -897,7 +906,7 @@ Route::set_solo (bool yn, void *src, bool group_override)
        */
 
        if (yn && Profile->get_trx()) {
-               set_mute (false, src);
+               set_mute (false, Controllable::UseGroup);
        }
 }
 
@@ -906,6 +915,7 @@ Route::set_self_solo (bool yn)
 {
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
        _self_solo = yn;
+       set_mute_master_solo ();
 }
 
 void
@@ -963,7 +973,7 @@ Route::mod_solo_by_others_upstream (int32_t delta)
        }
 
        set_mute_master_solo ();
-       solo_changed (false, this, false); /* EMIT SIGNAL */
+       solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
 }
 
 void
@@ -985,7 +995,7 @@ Route::mod_solo_by_others_downstream (int32_t delta)
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
 
        set_mute_master_solo ();
-       solo_changed (false, this, false); /* EMIT SIGNAL */
+       solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
 }
 
 void
@@ -996,7 +1006,7 @@ Route::set_mute_master_solo ()
 }
 
 void
-Route::mod_solo_isolated_by_upstream (bool yn, void* src)
+Route::mod_solo_isolated_by_upstream (bool yn)
 {
        bool old = solo_isolated ();
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n",
@@ -1015,19 +1025,19 @@ Route::mod_solo_isolated_by_upstream (bool yn, void* src)
        if (solo_isolated() != old) {
                /* solo isolated status changed */
                _mute_master->set_solo_ignore (solo_isolated());
-               solo_isolated_changed (src); /* EMIT SIGNAL */
+               solo_isolated_changed (); /* EMIT SIGNAL */
        }
 }
 
 void
-Route::set_solo_isolated (bool yn, void *src)
+Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override)
 {
        if (is_master() || is_monitor() || is_auditioner()) {
                return;
        }
 
-       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, _route_group));
+       if (use_group (group_override, &RouteGroup::is_solo)) {
+               _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::NoGroup));
                return;
        }
 
@@ -1065,13 +1075,13 @@ Route::set_solo_isolated (bool yn, void *src)
                bool does_feed = feeds (*i, &sends_only);
 
                if (does_feed && !sends_only) {
-                       (*i)->mod_solo_isolated_by_upstream (yn, src);
+                       (*i)->mod_solo_isolated_by_upstream (yn);
                }
        }
 
        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
-       solo_isolated_changed (src); /* EMIT SIGNAL */
+       solo_isolated_changed (); /* EMIT SIGNAL */
 }
 
 bool
@@ -1087,16 +1097,16 @@ Route::set_mute_points (MuteMaster::MutePoint mp)
        mute_points_changed (); /* EMIT SIGNAL */
 
        if (_mute_master->muted_by_self()) {
-               mute_changed (this); /* EMIT SIGNAL */
+               mute_changed (); /* EMIT SIGNAL */
                _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
 
 void
-Route::set_mute (bool yn, void *src)
+Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override)
 {
-       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) {
-               _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, _route_group));
+       if (use_group (group_override, &RouteGroup::is_mute)) {
+               _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::NoGroup));
                return;
        }
 
@@ -1107,7 +1117,7 @@ Route::set_mute (bool yn, void *src)
                */
                act_on_mute ();
                /* tell everyone else */
-               mute_changed (src); /* EMIT SIGNAL */
+               mute_changed (); /* EMIT SIGNAL */
                _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
@@ -1934,6 +1944,37 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
                if ((*p)->can_support_io_configuration(in, out)) {
                        DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
                        configuration.push_back(make_pair(in, out));
+
+                       if (is_monitor()) {
+                               // restriction for Monitor Section Processors
+                               if (in.n_audio() != out.n_audio() || out.n_midi() > 0) {
+                                       /* do not allow to add/remove channels (for now)
+                                        * The Monitor follows the master-bus and has no panner (unpan)
+                                        * but do allow processors with midi-in to be added (e.g VSTs with control that
+                                        * will remain unconnected)
+                                        */
+                                       DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration not allowed.\n");
+                                       return list<pair<ChanCount, ChanCount> > ();
+                               }
+                               if (boost::dynamic_pointer_cast<InternalSend> (*p)) {
+                                       // internal sends make no sense, only feedback
+                                       DEBUG_TRACE (DEBUG::Processors, "Monitor: No Sends allowed.\n");
+                                       return list<pair<ChanCount, ChanCount> > ();
+                               }
+                               if (boost::dynamic_pointer_cast<PortInsert> (*p)) {
+                                       /* External Sends can be problematic. one can add/remove ports
+                                        * there signal leaves the DAW to external monitors anyway, so there's
+                                        * no real use for allowing them here anyway.
+                                        */
+                                       DEBUG_TRACE (DEBUG::Processors, "Monitor: No External Sends allowed.\n");
+                                       return list<pair<ChanCount, ChanCount> > ();
+                               }
+                               if (boost::dynamic_pointer_cast<Send> (*p)) {
+                                       // ditto
+                                       DEBUG_TRACE (DEBUG::Processors, "Monitor: No Sends allowed.\n");
+                                       return list<pair<ChanCount, ChanCount> > ();
+                               }
+                       }
                        in = out;
                } else {
                        if (err) {
@@ -2246,6 +2287,11 @@ Route::get_template()
 XMLNode&
 Route::state(bool full_state)
 {
+       if (!_session._template_state_dir.empty()) {
+               assert (!full_state); // only for templates
+               foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), _session._template_state_dir));
+       }
+
        XMLNode *node = new XMLNode("Route");
        ProcessorList::iterator i;
        char buf[32];
@@ -2340,6 +2386,10 @@ Route::state(bool full_state)
                }
        }
 
+       if (!_session._template_state_dir.empty()) {
+               foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), ""));
+       }
+
        return *node;
 }
 
@@ -2455,11 +2505,11 @@ Route::set_state (const XMLNode& node, int version)
        }
 
        if ((prop = node.property ("solo-isolated")) != 0) {
-               set_solo_isolated (string_is_affirmative (prop->value()), this);
+               set_solo_isolated (string_is_affirmative (prop->value()), Controllable::NoGroup);
        }
 
        if ((prop = node.property ("solo-safe")) != 0) {
-               set_solo_safe (string_is_affirmative (prop->value()), this);
+               set_solo_safe (string_is_affirmative (prop->value()), Controllable::NoGroup);
        }
 
        if ((prop = node.property (X_("phase-invert"))) != 0) {
@@ -2614,7 +2664,7 @@ Route::set_state_2X (const XMLNode& node, int version)
 
                /* XXX force reset of solo status */
 
-               set_solo (yn, this);
+               set_solo (yn);
        }
 
        if ((prop = node.property (X_("muted"))) != 0) {
@@ -2753,7 +2803,7 @@ Route::set_state_2X (const XMLNode& node, int version)
                                gain_t val;
 
                                if (sscanf (prop->value().c_str(), "%f", &val) == 1) {
-                                       _amp->gain_control()->set_value (val);
+                                       _amp->gain_control()->set_value (val, Controllable::NoGroup);
                                }
                        }
 
@@ -3181,7 +3231,7 @@ void
 Route::set_comment (string cmt, void *src)
 {
        _comment = cmt;
-       comment_changed (src);
+       comment_changed ();
        _session.set_dirty ();
 }
 
@@ -3355,7 +3405,7 @@ Route::input_change_handler (IOChange change, void * /*src*/)
                if (_solo_isolated_by_upstream) {
                        // solo-isolate currently only propagates downstream
                        if (idelta < 0) {
-                               mod_solo_isolated_by_upstream (false, this);
+                               mod_solo_isolated_by_upstream (false);
                        }
                        // TODO think: mod_solo_isolated_by_upstream() does not take delta arg,
                        // but idelta can't be smaller than -1, can it?
@@ -3375,7 +3425,7 @@ Route::input_change_handler (IOChange change, void * /*src*/)
                        }
 
                        if (idelta < 0 && does_feed && !sends_only) {
-                               (*i)->mod_solo_isolated_by_upstream (false, this);
+                               (*i)->mod_solo_isolated_by_upstream (false);
                        }
                }
        }
@@ -3834,12 +3884,82 @@ Route::set_latency_compensation (framecnt_t longest_session_latency)
        }
 }
 
-Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(),
-                            Evoral::Parameter (SoloAutomation),
-                            ParameterDescriptor(Evoral::Parameter (SoloAutomation)),
-                            boost::shared_ptr<AutomationList>(), name)
+void
+Route::set_control (AutomationType type, double val, PBD::Controllable::GroupControlDisposition group_override)
+{
+       boost::shared_ptr<RouteList> rl;
+
+       switch (type) {
+       case GainAutomation:
+               /* route must mediate group control */
+               set_gain (val, group_override);
+               return;
+               break;
+
+       case TrimAutomation:
+               /* route must mediate group control */
+               set_trim (val, group_override);
+               return;
+               break;
+
+       case RecEnableAutomation:
+               /* session must mediate group control */
+               rl.reset (new RouteList);
+               rl->push_back (shared_from_this());
+               _session.set_record_enabled (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override);
+               return;
+               break;
+
+       case SoloAutomation:
+               /* session must mediate group control */
+               rl.reset (new RouteList);
+               rl->push_back (shared_from_this());
+               if (Config->get_solo_control_is_listen_control()) {
+                       _session.set_listen (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override);
+               } else {
+                       _session.set_solo (rl, val >= 0.5 ? true : false);
+               }
+
+               return;
+               break;
+
+       case MuteAutomation:
+               /* session must mediate group control */
+               rl.reset (new RouteList);
+               rl->push_back (shared_from_this());
+               _session.set_mute (rl, !muted(), Session::rt_cleanup, group_override);
+               return;
+               break;
+
+       default:
+               /* Not a route automation control */
+               fatal << string_compose (_("programming error: %1%2\n"), X_("illegal type of route automation control passed to Route::set_control(): "), enum_2_string(type)) << endmsg;
+               /*NOTREACHED*/
+               return;
+       }
+}
+
+
+Route::RouteAutomationControl::RouteAutomationControl (const std::string& name,
+                                                       AutomationType atype,
+                                                       boost::shared_ptr<AutomationList> alist,
+                                                       boost::shared_ptr<Route> r)
+       : AutomationControl (r->session(), Evoral::Parameter (atype),
+                            ParameterDescriptor (Evoral::Parameter (atype)),
+                            alist, name)
        , _route (r)
+{
+}
+
+Route::GainControllable::GainControllable (Session& s, AutomationType atype, boost::shared_ptr<Route> r)
+       : GainControl (s, Evoral::Parameter(atype))
+       , _route (r)
+{
+
+}
+
+Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
+       : RouteAutomationControl (name, SoloAutomation, boost::shared_ptr<AutomationList>(), r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
        gl->set_interpolation(Evoral::ControlList::Discrete);
@@ -3847,7 +3967,15 @@ Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<R
 }
 
 void
-Route::SoloControllable::set_value (double val)
+Route::SoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
+{
+       if (writable()) {
+               _set_value (val, group_override);
+       }
+}
+
+void
+Route::SoloControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
 {
        const bool bval = ((val >= 0.5) ? true : false);
 
@@ -3861,12 +3989,20 @@ Route::SoloControllable::set_value (double val)
        rl->push_back (r);
 
        if (Config->get_solo_control_is_listen_control()) {
-               _session.set_listen (rl, bval);
+               _session.set_listen (rl, bval, Session::rt_cleanup, group_override);
        } else {
-               _session.set_solo (rl, bval);
+               _session.set_solo (rl, bval, Session::rt_cleanup, group_override);
        }
 }
 
+void
+Route::SoloControllable::set_value_unchecked (double val)
+{
+       /* Used only by automation playback */
+
+       _set_value (val, Controllable::NoGroup);
+}
+
 double
 Route::SoloControllable::get_value () const
 {
@@ -3883,11 +4019,7 @@ Route::SoloControllable::get_value () const
 }
 
 Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(),
-                            Evoral::Parameter (MuteAutomation),
-                            ParameterDescriptor (Evoral::Parameter (MuteAutomation)),
-                            boost::shared_ptr<AutomationList>(),
-                            name)
+       : RouteAutomationControl (name, MuteAutomation, boost::shared_ptr<AutomationList>(), r)
        , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
@@ -3901,13 +4033,41 @@ Route::MuteControllable::set_superficial_value(bool muted)
        /* Note we can not use AutomationControl::set_value here since it will emit
           Changed(), but the value will not be correct to the observer. */
 
-       bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
+       const bool to_list = _list && ((AutomationList*)_list.get ())->automation_write ();
+       const double where = _session.audible_frame ();
+       if (to_list) {
+               /* Note that we really need this:
+                *  if (as == Touch && _list->in_new_write_pass ()) {
+                *       alist->start_write_pass (_session.audible_frame ());
+                *  }
+                * here in the case of the user calling from a GUI or whatever.
+                * Without the ability to distinguish between user and
+                * automation-initiated changes, we lose the "touch mute"
+                * behaviour we have in AutomationController::toggled ().
+                */
+               _list->set_in_write_pass (true, false, where);
+       }
+
+       Control::set_double (muted, where, to_list);
+}
+
+void
+Route::MuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
+{
+       if (writable()) {
+               _set_value (val, group_override);
+       }
+}
 
-       Control::set_double (muted, _session.transport_frame(), to_list);
+void
+Route::MuteControllable::set_value_unchecked (double val)
+{
+       /* used only automation playback */
+       _set_value (val, Controllable::NoGroup);
 }
 
 void
-Route::MuteControllable::set_value (double val)
+Route::MuteControllable::_set_value (double val, Controllable::GroupControlDisposition group_override)
 {
        const bool bval = ((val >= 0.5) ? true : false);
 
@@ -3917,17 +4077,16 @@ Route::MuteControllable::set_value (double val)
        }
 
        if (_list && ((AutomationList*)_list.get())->automation_playback()) {
+               // Set superficial/automation value to drive controller (and possibly record)
+               set_superficial_value (bval);
                // Playing back automation, set route mute directly
-               r->set_mute (bval, this);
+               r->set_mute (bval, Controllable::NoGroup);
        } else {
                // Set from user, queue mute event
                boost::shared_ptr<RouteList> rl (new RouteList);
                rl->push_back (r);
-               _session.set_mute (rl, bval, Session::rt_cleanup);
+               _session.set_mute (rl, bval, Session::rt_cleanup, group_override);
        }
-
-       // Set superficial/automation value to drive controller (and possibly record)
-       set_superficial_value(bval);
 }
 
 double
@@ -3943,6 +4102,46 @@ Route::MuteControllable::get_value () const
        return (r && r->muted()) ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
 }
 
+Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr<Route> r)
+       : RouteAutomationControl (name, PhaseAutomation, boost::shared_ptr<AutomationList>(), r)
+{
+       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(PhaseAutomation)));
+       gl->set_interpolation(Evoral::ControlList::Discrete);
+       set_list (gl);
+}
+
+void
+Route::PhaseControllable::set_value (double v, PBD::Controllable::GroupControlDisposition /* group_override */)
+{
+       boost::shared_ptr<Route> r = _route.lock ();
+       if (r->phase_invert().size()) {
+               if (v == 0 || (v < 1 && v > 0.9) ) {
+                       r->set_phase_invert (_current_phase, false);
+               } else {
+                       r->set_phase_invert (_current_phase, true);
+               }
+       }
+}
+
+double
+Route::PhaseControllable::get_value () const
+{
+       boost::shared_ptr<Route> r = _route.lock ();
+       return (double) r->phase_invert (_current_phase);
+}
+
+void
+Route::PhaseControllable::set_channel (uint32_t c)
+{
+       _current_phase = c;
+}
+
+uint32_t
+Route::PhaseControllable::channel () const
+{
+       return _current_phase;
+}
+
 void
 Route::set_block_size (pframes_t nframes)
 {
@@ -4045,17 +4244,33 @@ Route::shift (framepos_t pos, framecnt_t frames)
        }
 }
 
+void
+Route::set_plugin_state_dir (boost::weak_ptr<Processor> p, const std::string& d)
+{
+       boost::shared_ptr<Processor> processor (p.lock ());
+       boost::shared_ptr<PluginInsert> pi  = boost::dynamic_pointer_cast<PluginInsert> (processor);
+       if (!pi) {
+               return;
+       }
+       pi->set_state_dir (d);
+}
 
 int
 Route::save_as_template (const string& path, const string& name)
 {
+       std::string state_dir = path.substr (0, path.find_last_of ('.')); // strip template_suffix
+       PBD::Unwinder<std::string> uw (_session._template_state_dir, state_dir);
+
        XMLNode& node (state (false));
+
        XMLTree tree;
 
        IO::set_name_in_state (*node.children().front(), name);
 
        tree.set_root (&node);
-       return tree.write (path.c_str());
+
+       /* return zero on success, non-zero otherwise */
+       return !tree.write (path.c_str());
 }
 
 
@@ -4096,7 +4311,7 @@ Route::set_name (const string& str)
  *  @param name New name.
  */
 void
-Route::set_name_in_state (XMLNode& node, string const & name)
+Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playlist)
 {
        node.add_property (X_("name"), name);
 
@@ -4116,7 +4331,9 @@ Route::set_name_in_state (XMLNode& node, string const & name)
 
                } else if ((*i)->name() == X_("Diskstream")) {
 
-                       (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+                       if (rename_playlist) {
+                               (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+                       }
                        (*i)->add_property (X_("name"), name);
 
                }
@@ -4231,10 +4448,16 @@ Route::panner_shell() const
        return _main_outs->panner_shell();
 }
 
-boost::shared_ptr<AutomationControl>
+boost::shared_ptr<GainControl>
 Route::gain_control() const
 {
-       return _amp->gain_control();
+       return _gain_control;
+}
+
+boost::shared_ptr<GainControl>
+Route::trim_control() const
+{
+       return _trim_control;
 }
 
 boost::shared_ptr<AutomationControl>
@@ -4634,7 +4857,7 @@ Route::setup_invisible_processors ()
 
        if (_monitor_control && is_monitor ()) {
                assert (!_monitor_control->display_to_user ());
-               new_processors.push_front (_monitor_control);
+               new_processors.insert (amp, _monitor_control);
        }
 
        /* INTERNAL RETURN */
@@ -4914,3 +5137,362 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
                bufs.set_count (io->n_ports());
        }
 }
+
+boost::shared_ptr<AutomationControl>
+Route::pan_azimuth_control() const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+       assert (plug);
+       const uint32_t port_channel_post_pan = 2; // gtk2_ardour/mixbus_ports.h
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_pan)));
+#else
+       if (!_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+       return _pannable->pan_azimuth_control;
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::pan_elevation_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanElevationAutomation) != c.end()) {
+               return _pannable->pan_elevation_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+boost::shared_ptr<AutomationControl>
+Route::pan_width_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanWidthAutomation) != c.end()) {
+               return _pannable->pan_width_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+boost::shared_ptr<AutomationControl>
+Route::pan_frontback_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanFrontBackAutomation) != c.end()) {
+               return _pannable->pan_frontback_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+boost::shared_ptr<AutomationControl>
+Route::pan_lfe_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanLFEAutomation) != c.end()) {
+               return _pannable->pan_lfe_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+
+uint32_t
+Route::eq_band_cnt () const
+{
+       if (Profile->get_mixbus()) {
+               return 3;
+       } else {
+               /* Ardour has no well-known EQ object */
+               return 0;
+       }
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_gain_controllable (uint32_t band) const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       uint32_t port_number;
+       switch (band) {
+       case 0:
+               if (is_master() || mixbus()) {
+                       port_number = 4;
+               } else {
+                       port_number = 8;
+               }
+               break;
+       case 1:
+               if (is_master() || mixbus()) {
+                       port_number = 3;
+               } else {
+                       port_number = 6;
+               }
+               break;
+       case 2:
+               if (is_master() || mixbus()) {
+                       port_number = 2;
+               } else {
+                       port_number = 4;
+               }
+               break;
+       default:
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::eq_freq_controllable (uint32_t band) const
+{
+#ifdef MIXBUS
+
+       if (mixbus() || is_master()) {
+               /* no frequency controls for mixbusses or master */
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       uint32_t port_number;
+       switch (band) {
+       case 0:
+               port_number = 7;
+               break;
+       case 1:
+               port_number = 5;
+               break;
+       case 2:
+               port_number = 3;
+               break;
+       default:
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_q_controllable (uint32_t band) const
+{
+       return boost::shared_ptr<AutomationControl>();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_shape_controllable (uint32_t band) const
+{
+       return boost::shared_ptr<AutomationControl>();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_enable_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 1)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_hpf_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+string
+Route::eq_band_name (uint32_t band) const
+{
+       if (Profile->get_mixbus()) {
+               switch (band) {
+               case 0:
+                       return _("lo");
+               case 1:
+                       return _("mid");
+               case 2:
+                       return _("hi");
+               default:
+                       return string();
+               }
+       } else {
+               return string ();
+       }
+}
+
+boost::shared_ptr<AutomationControl>
+Route::comp_enable_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 1)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_threshold_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2)));
+
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_speed_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 3)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_mode_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 4)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_makeup_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 5)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_redux_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+string
+Route::comp_mode_name (uint32_t mode) const
+{
+#ifdef MIXBUS
+       switch (mode) {
+       case 0:
+               return _("Leveler");
+       case 1:
+               return _("Compressor");
+       case 2:
+               return _("Limiter");
+       }
+
+       return _("???");
+#else
+       return _("???");
+#endif
+}
+
+string
+Route::comp_speed_name (uint32_t mode) const
+{
+#ifdef MIXBUS
+       switch (mode) {
+       case 0:
+               return _("Attk");
+       case 1:
+               return _("Ratio");
+       case 2:
+               return _("Rels");
+       }
+       return _("???");
+#else
+       return _("???");
+#endif
+}