add a new constructor for RouteAutomationControllable that takes a ParameterDescriptor
[ardour.git] / libs / ardour / route.cc
index 5abe96b2638e13ef44e5429f9067dd143e9703c8..da0183304ce4681a89b3fe275ac61f4afb9756b8 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"
@@ -56,6 +57,7 @@
 #include "ardour/pannable.h"
 #include "ardour/panner.h"
 #include "ardour/panner_shell.h"
+#include "ardour/parameter_descriptor.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
 #include "ardour/port_insert.h"
@@ -171,15 +173,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");
+               _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)) {
@@ -379,19 +388,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;
                        }
@@ -422,34 +442,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
@@ -771,19 +785,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;
        }
 
@@ -798,7 +807,7 @@ Route::set_listen (bool yn, void* src, bool group_override)
                        }
                        _mute_master->set_soloed_by_others (false);
 
-                       listen_changed (src, group_override); /* EMIT SIGNAL */
+                       listen_changed (group_override); /* EMIT SIGNAL */
                }
        }
 }
@@ -814,11 +823,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 ();
        }
 }
 
@@ -855,17 +864,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()));
@@ -877,21 +886,17 @@ Route::set_solo (bool yn, void *src, bool group_override)
                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_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);
-               solo_changed (true, src, group_override); /* EMIT SIGNAL */
+               solo_changed (true, group_override); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
 
@@ -902,7 +907,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);
        }
 }
 
@@ -969,7 +974,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
@@ -991,7 +996,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
@@ -1002,7 +1007,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",
@@ -1021,19 +1026,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;
        }
 
@@ -1071,13 +1076,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
@@ -1093,16 +1098,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;
        }
 
@@ -1113,7 +1118,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 */
        }
 }
@@ -2501,11 +2506,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) {
@@ -2660,7 +2665,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) {
@@ -3227,7 +3232,7 @@ void
 Route::set_comment (string cmt, void *src)
 {
        _comment = cmt;
-       comment_changed (src);
+       comment_changed ();
        _session.set_dirty ();
 }
 
@@ -3401,7 +3406,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?
@@ -3421,7 +3426,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);
                        }
                }
        }
@@ -3880,243 +3885,6 @@ Route::set_latency_compensation (framecnt_t longest_session_latency)
        }
 }
 
-void
-Route::set_control (RouteAutomationControl& control, double val, PBD::Controllable::GroupControlDisposition /*group_override*/)
-{
-       boost::shared_ptr<RouteList> rl;
-
-       switch (control.parameter().type()) {
-       case GainAutomation:
-               /* route must mediate group control */
-               set_gain (val, this); /* any "src" argument will do other than our route group */
-               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);
-               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);
-               } 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());
-               return;
-               break;
-
-       case PanAzimuthAutomation:
-       case PanElevationAutomation:
-       case PanWidthAutomation:
-       case PanFrontBackAutomation:
-       case PanLFEAutomation:
-               break;
-
-       default:
-               /* Not a route automation control */
-               return;
-       }
-
-       control.route_set_value (val);
-}
-
-
-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::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);
-       set_list (gl);
-}
-
-void
-Route::SoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition /* group_override */)
-{
-       if (writable()) {
-               set_value_unchecked (val);
-       }
-}
-
-void
-Route::SoloControllable::set_value_unchecked (double val)
-{
-       const bool bval = ((val >= 0.5) ? true : false);
-
-       boost::shared_ptr<RouteList> rl (new RouteList);
-
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return;
-       }
-
-       rl->push_back (r);
-
-       if (Config->get_solo_control_is_listen_control()) {
-               _session.set_listen (rl, bval);
-       } else {
-               _session.set_solo (rl, bval);
-       }
-}
-
-double
-Route::SoloControllable::get_value () const
-{
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return 0;
-       }
-
-       if (Config->get_solo_control_is_listen_control()) {
-               return r->listening_via_monitor() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
-       } else {
-               return r->self_soloed() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
-       }
-}
-
-Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
-       : RouteAutomationControl (name, MuteAutomation, boost::shared_ptr<AutomationList>(), r)
-       , _route (r)
-{
-       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
-       gl->set_interpolation(Evoral::ControlList::Discrete);
-       set_list (gl);
-}
-
-void
-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. */
-
-       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_unchecked (val);
-       }
-}
-
-void
-Route::MuteControllable::set_value_unchecked (double val)
-{
-       const bool bval = ((val >= 0.5) ? true : false);
-
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return;
-       }
-
-       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);
-       } 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);
-       }
-}
-
-double
-Route::MuteControllable::get_value () const
-{
-       if (_list && ((AutomationList*)_list.get())->automation_playback()) {
-               // Playing back automation, get the value from the list
-               return AutomationControl::get_value();
-       }
-
-       // Not playing back automation, get the actual route mute value
-       boost::shared_ptr<Route> r = _route.lock ();
-       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)
 {
@@ -4423,16 +4191,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<AutomationControl>
+boost::shared_ptr<GainControl>
 Route::trim_control() const
 {
-       return _trim->gain_control();
+       return _trim_control;
 }
 
 boost::shared_ptr<AutomationControl>
@@ -4466,10 +4234,10 @@ Route::get_control (const Evoral::Parameter& param)
 }
 
 boost::shared_ptr<Processor>
-Route::nth_plugin (uint32_t n)
+Route::nth_plugin (uint32_t n) const
 {
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-       ProcessorList::iterator i;
+       ProcessorList::const_iterator i;
 
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
@@ -4483,13 +4251,21 @@ Route::nth_plugin (uint32_t n)
 }
 
 boost::shared_ptr<Processor>
-Route::nth_send (uint32_t n)
+Route::nth_send (uint32_t n) const
 {
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-       ProcessorList::iterator i;
+       ProcessorList::const_iterator i;
 
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<Send> (*i)) {
+
+                       if ((*i)->name() == _("Monitor 1")) {
+                               /* send to monitor section is not considered
+                                  to be an accessible send.
+                               */
+                               continue;
+                       }
+
                        if (n-- == 0) {
                                return *i;
                        }
@@ -5118,7 +4894,9 @@ Route::pan_azimuth_control() const
 {
 #ifdef MIXBUS
        boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
-       assert (plug);
+       if (!plug) {
+               return boost::shared_ptr<AutomationControl>();
+       }
        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
@@ -5471,3 +5249,88 @@ Route::comp_speed_name (uint32_t mode) const
        return _("???");
 #endif
 }
+
+boost::shared_ptr<AutomationControl>
+Route::send_level_controllable (uint32_t n) const
+{
+#ifdef  MIXBUS
+       boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+       if (!plug) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       if (n >= 8) {
+               /* no such bus */
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       const uint32_t port_id = port_channel_post_aux1_level + (2*n); // gtk2_ardour/mixbus_ports.h
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+#else
+       boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(nth_send (n));
+       if (!s) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+       return s->gain_control ();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::send_enable_controllable (uint32_t n) const
+{
+#ifdef  MIXBUS
+       boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+       if (!plug) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       if (n >= 8) {
+               /* no such bus */
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       const uint32_t port_id = port_channel_post_aux1_asgn + (2*n); // gtk2_ardour/mixbus_ports.h
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+#else
+       /* although Ardour sends have enable/disable as part of the Processor
+          API, it is not exposed as a controllable.
+
+          XXX: we should fix this.
+       */
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+string
+Route::send_name (uint32_t n) const
+{
+#ifdef MIXBUS
+       if (n >= 8) {
+               return string();
+       }
+       boost::shared_ptr<Route> r = _session.get_mixbus (n);
+       assert (r);
+       return r->name();
+#else
+       boost::shared_ptr<Processor> p = nth_send (n);
+       if (p) {
+               return p->name();
+       } else {
+               return string();
+       }
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::master_send_enable_controllable () const
+{
+#ifdef  MIXBUS
+       boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+       if (!plug) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_mstr_assign)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}