2 Copyright (C) 2006,2007 John Anderson
3 Copyright (C) 2012 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "midi++/port.h"
28 #include "pbd/compose.h"
29 #include "pbd/convert.h"
31 #include "ardour/amp.h"
32 #include "ardour/bundle.h"
33 #include "ardour/debug.h"
34 #include "ardour/midi_ui.h"
35 #include "ardour/meter.h"
36 #include "ardour/pannable.h"
37 #include "ardour/panner.h"
38 #include "ardour/panner_shell.h"
39 #include "ardour/rc_configuration.h"
40 #include "ardour/route.h"
41 #include "ardour/session.h"
42 #include "ardour/send.h"
43 #include "ardour/track.h"
44 #include "ardour/user_bundle.h"
46 #include "mackie_control_protocol.h"
47 #include "surface_port.h"
56 using namespace Mackie;
58 using namespace ARDOUR;
61 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
62 #define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__))
64 extern PBD::EventLoop::InvalidationRecord* __invalidator (sigc::trackable& trackable, const char*, int);
65 #define invalidator() __invalidator (*(MackieControlProtocol::instance()), __FILE__, __LINE__)
67 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
76 , _vpot_mode (PanAzimuth)
77 , _preflip_vpot_mode (PanAzimuth)
81 , _controls_locked (false)
82 , _reset_display_at (0)
83 , _last_gain_position_written (-1.0)
84 , _last_pan_position_written (-1.0)
86 _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
87 _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
88 _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
91 for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
92 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
93 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
94 _surface->number(), index, Button::id_to_name (bb->bid()),
95 bb->id(), b->second.base_id));
102 /* surface is responsible for deleting all controls */
106 Strip::add (Control & control)
110 Group::add (control);
112 /* fader, vpot, meter were all set explicitly */
114 if ((button = dynamic_cast<Button*>(&control)) != 0) {
115 switch (button->bid()) {
116 case Button::RecEnable:
128 case Button::VSelect:
131 case Button::FaderTouch:
132 _fader_touch = button;
140 Strip::set_route (boost::shared_ptr<Route> r, bool with_messages)
142 if (_controls_locked) {
146 route_connections.drop_connections ();
148 _solo->set_control (boost::shared_ptr<AutomationControl>());
149 _mute->set_control (boost::shared_ptr<AutomationControl>());
150 _select->set_control (boost::shared_ptr<AutomationControl>());
151 _recenable->set_control (boost::shared_ptr<AutomationControl>());
152 _fader->set_control (boost::shared_ptr<AutomationControl>());
153 _vpot->set_control (boost::shared_ptr<AutomationControl>());
161 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping route %3\n",
162 _surface->number(), _index, _route->name()));
164 _solo->set_control (_route->solo_control());
165 _mute->set_control (_route->mute_control());
166 set_vpot_mode (PanAzimuth);
168 _route->solo_control()->Changed.connect(route_connections, invalidator(), ui_bind (&Strip::notify_solo_changed, this), ui_context());
169 _route->mute_control()->Changed.connect(route_connections, invalidator(), ui_bind (&Strip::notify_mute_changed, this), ui_context());
171 boost::shared_ptr<Pannable> pannable = _route->pannable();
174 pannable->pan_azimuth_control->Changed.connect(route_connections, invalidator(), ui_bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
175 pannable->pan_width_control->Changed.connect(route_connections, invalidator(), ui_bind (&Strip::notify_panner_width_changed, this, false), ui_context());
177 _route->gain_control()->Changed.connect(route_connections, invalidator(), ui_bind (&Strip::notify_gain_changed, this, false), ui_context());
178 _route->PropertyChanged.connect (route_connections, invalidator(), ui_bind (&Strip::notify_property_changed, this, _1), ui_context());
180 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
183 _recenable->set_control (trk->rec_enable_control());
184 trk->rec_enable_control()->Changed .connect(route_connections, invalidator(), ui_bind (&Strip::notify_record_enable_changed, this), ui_context());
187 // TODO this works when a currently-banked route is made inactive, but not
188 // when a route is activated which should be currently banked.
190 _route->active_changed.connect (route_connections, invalidator(), ui_bind (&Strip::notify_active_changed, this), ui_context());
191 _route->DropReferences.connect (route_connections, invalidator(), ui_bind (&Strip::notify_route_deleted, this), ui_context());
197 /* setup legal VPot modes for this route */
199 build_input_list (_route->input()->n_ports());
200 build_output_list (_route->output()->n_ports());
202 current_pot_modes.clear();
205 boost::shared_ptr<Panner> panner = pannable->panner();
207 set<Evoral::Parameter> automatable = panner->what_can_be_automated ();
208 set<Evoral::Parameter>::iterator a;
210 if ((a = automatable.find (PanAzimuthAutomation)) != automatable.end()) {
211 current_pot_modes.push_back (PanAzimuth);
214 if ((a = automatable.find (PanWidthAutomation)) != automatable.end()) {
215 current_pot_modes.push_back (PanWidth);
229 notify_solo_changed ();
230 notify_mute_changed ();
231 notify_gain_changed ();
232 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
233 notify_panner_azi_changed ();
234 notify_panner_width_changed ();
235 notify_record_enable_changed ();
239 Strip::notify_solo_changed ()
241 if (_route && _solo) {
242 _surface->write (_solo->set_state (_route->soloed() ? on : off));
247 Strip::notify_mute_changed ()
249 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
250 if (_route && _mute) {
251 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\troute muted ? %1\n", _route->muted()));
252 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_route->muted() ? on : off)));
254 _surface->write (_mute->set_state (_route->muted() ? on : off));
259 Strip::notify_record_enable_changed ()
261 if (_route && _recenable) {
262 _surface->write (_recenable->set_state (_route->record_enabled() ? on : off));
267 Strip::notify_active_changed ()
269 _surface->mcp().refresh_current_bank();
273 Strip::notify_route_deleted ()
275 _surface->mcp().refresh_current_bank();
279 Strip::notify_gain_changed (bool force_update)
285 if (_surface->mcp().flip_mode()) {
292 boost::shared_ptr<AutomationControl> ac = _route->gain_control();
294 float gain_coefficient = ac->get_value();
295 float normalized_position = ac->internal_to_interface (gain_coefficient);
297 if (force_update || normalized_position != _last_gain_position_written) {
299 if (_surface->mcp().flip_mode()) {
300 if (!control->in_use()) {
301 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
303 do_parameter_display (GainAutomation, gain_coefficient);
305 if (!control->in_use()) {
306 _surface->write (_fader->set_position (normalized_position));
308 do_parameter_display (GainAutomation, gain_coefficient);
311 queue_display_reset (2000);
312 _last_gain_position_written = normalized_position;
318 Strip::notify_property_changed (const PropertyChange& what_changed)
320 if (!what_changed.contains (ARDOUR::Properties::name)) {
326 string fullname = _route->name();
328 if (fullname.length() <= 6) {
331 line1 = PBD::short_version (fullname, 6);
334 _surface->write (display (0, line1));
339 Strip::notify_panner_azi_changed (bool force_update)
343 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
345 boost::shared_ptr<Pannable> pannable = _route->pannable();
348 _surface->write (_vpot->zero());
354 if (_surface->mcp().flip_mode()) {
357 if (_vpot_mode != PanAzimuth) {
363 if (!control->in_use()) {
365 double pos = pannable->pan_azimuth_control->internal_to_interface (pannable->pan_azimuth_control->get_value());
367 if (force_update || pos != _last_pan_position_written) {
369 if (_surface->mcp().flip_mode()) {
371 _surface->write (_fader->set_position (pos));
372 do_parameter_display (PanAzimuthAutomation, pos);
374 _surface->write (_vpot->set (pos, true, Pot::dot));
375 do_parameter_display (PanAzimuthAutomation, pos);
378 queue_display_reset (2000);
379 _last_pan_position_written = pos;
386 Strip::notify_panner_width_changed (bool force_update)
390 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
392 boost::shared_ptr<Pannable> pannable = _route->pannable();
395 _surface->write (_vpot->zero());
401 if (_surface->mcp().flip_mode()) {
404 if (_vpot_mode != PanWidth) {
410 if (!control->in_use()) {
412 double pos = pannable->pan_width_control->internal_to_interface (pannable->pan_width_control->get_value());
414 if (force_update || pos != _last_pan_position_written) {
416 if (_surface->mcp().flip_mode()) {
418 _surface->write (_fader->set_position (pos));
419 do_parameter_display (PanWidthAutomation, pos);
421 _surface->write (_vpot->set (pos, true, Pot::spread));
422 do_parameter_display (PanWidthAutomation, pos);
425 queue_display_reset (2000);
426 _last_pan_position_written = pos;
433 Strip::select_event (Button& button, ButtonState bs)
435 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
439 int ms = _surface->mcp().modifier_state();
441 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
442 _controls_locked = !_controls_locked;
443 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
444 queue_display_reset (1000);
448 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
449 /* reset to default */
450 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
452 ac->set_value (ac->normal());
457 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
458 _surface->mcp().add_down_select_button (_surface->number(), _index);
459 _surface->mcp().select_range ();
462 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
463 _surface->mcp().remove_down_select_button (_surface->number(), _index);
468 Strip::vselect_event (Button& button, ButtonState bs)
473 int ms = _surface->mcp().modifier_state();
475 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
476 boost::shared_ptr<AutomationControl> ac = button.control ();
480 /* reset to default/normal value */
481 ac->set_value (ac->normal());
485 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
493 Strip::fader_touch_event (Button& button, ButtonState bs)
495 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
497 /* never use the modified control for fader stuff */
501 _fader->set_in_use (true);
502 _fader->start_touch (_surface->mcp().transport_frame());
503 boost::shared_ptr<AutomationControl> ac = _fader->control ();
505 do_parameter_display ((AutomationType) ac->parameter().type(), ac->internal_to_interface (ac->get_value()));
506 queue_display_reset (2000);
511 _fader->set_in_use (false);
512 _fader->stop_touch (_surface->mcp().transport_frame(), true);
519 Strip::handle_button (Button& button, ButtonState bs)
521 boost::shared_ptr<AutomationControl> control;
524 button.set_in_use (true);
526 button.set_in_use (false);
529 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
531 switch (button.bid()) {
533 select_event (button, bs);
536 case Button::VSelect:
537 vselect_event (button, bs);
540 case Button::FaderTouch:
541 fader_touch_event (button, bs);
545 if ((control = button.control ())) {
547 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
548 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
551 int ms = _surface->mcp().modifier_state();
553 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
554 /* reset to default/normal value */
555 new_value = control->normal();
557 new_value = control->get_value() ? 0.0 : 1.0;
560 /* get all controls that either have their
561 * button down or are within a range of
562 * several down buttons
565 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type());
568 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
569 controls.size(), control->parameter().type(), new_value));
573 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
574 (*c)->set_value (new_value);
578 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
579 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
587 Strip::do_parameter_display (AutomationType type, float val)
592 _surface->write (display (1, " -inf "));
595 float dB = accurate_coefficient_to_dB (val);
596 snprintf (buf, sizeof (buf), "%6.1f", dB);
597 _surface->write (display (1, buf));
601 case PanAzimuthAutomation:
603 boost::shared_ptr<Pannable> p = _route->pannable();
604 if (p && p->panner()) {
605 string str = p->panner()->value_as_string (p->pan_azimuth_control);
606 _surface->write (display (1, str));
611 case PanWidthAutomation:
614 snprintf (buf, sizeof (buf), "%5ld%%", lrintf (val * 100.0));
615 _surface->write (display (1, buf));
625 Strip::handle_fader (Fader& fader, float position)
627 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
629 fader.set_value (position);
630 fader.start_touch (_surface->mcp().transport_frame());
631 queue_display_reset (2000);
633 // must echo bytes back to slider now, because
634 // the notifier only works if the fader is not being
635 // touched. Which it is if we're getting input.
637 _surface->write (fader.set_position (position));
641 Strip::handle_pot (Pot& pot, float delta)
643 /* Pots only emit events when they move, not when they
644 stop moving. So to get a stop event, we need to use a timeout.
647 boost::shared_ptr<AutomationControl> ac = pot.control();
648 double p = pot.get_value ();
650 p = max (ac->lower(), p);
651 p = min (ac->upper(), p);
656 Strip::periodic (uint64_t usecs)
662 update_automation ();
665 if (_reset_display_at && _reset_display_at < usecs) {
671 Strip::update_automation ()
673 ARDOUR::AutoState gain_state = _route->gain_control()->automation_state();
675 if (gain_state == Touch || gain_state == Play) {
676 notify_gain_changed (false);
679 if (_route->panner()) {
680 ARDOUR::AutoState panner_state = _route->panner()->automation_state();
681 if (panner_state == Touch || panner_state == Play) {
682 notify_panner_azi_changed (false);
683 notify_panner_width_changed (false);
689 Strip::update_meter ()
692 float dB = const_cast<PeakMeter&> (_route->peak_meter()).peak_power (0);
693 _surface->write (_meter->update_message (dB));
700 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
701 _surface->write ((*it)->zero ());
704 _surface->write (blank_display (0));
705 _surface->write (blank_display (1));
709 Strip::blank_display (uint32_t line_number)
711 return display (line_number, string());
715 Strip::display (uint32_t line_number, const std::string& line)
717 assert (line_number <= 1);
719 MidiByteArray retval;
721 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
724 retval << _surface->sysex_hdr();
728 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
729 retval << (_index * 7 + (line_number * 0x38));
731 // ascii data to display
733 // pad with " " out to 6 chars
734 for (int i = line.length(); i < 6; ++i) {
738 // column spacer, unless it's the right-hand column
750 Strip::lock_controls ()
752 _controls_locked = true;
756 Strip::unlock_controls ()
758 _controls_locked = false;
762 Strip::gui_selection_changed (ARDOUR::RouteNotificationListPtr rl)
764 for (ARDOUR::RouteNotificationList::iterator i = rl->begin(); i != rl->end(); ++i) {
765 if ((*i) == _route) {
766 return _select->set_state (on);
770 return _select->set_state (off);
774 Strip::vpot_mode_string () const
776 switch (_vpot_mode) {
815 Strip::flip_mode_changed (bool notify)
821 if (_surface->mcp().flip_mode()) {
822 /* flip mode is on - save what it used to be */
823 _preflip_vpot_mode = _vpot_mode;
825 /* flip mode is off - restore flip mode to what it used to be */
826 _vpot_mode = _preflip_vpot_mode;
829 boost::shared_ptr<AutomationControl> fader_controllable = _fader->control ();
830 boost::shared_ptr<AutomationControl> vpot_controllable = _vpot->control ();
832 _fader->set_control (vpot_controllable);
833 _vpot->set_control (fader_controllable);
835 _surface->write (display (1, vpot_mode_string ()));
843 Strip::queue_display_reset (uint32_t msecs)
846 struct timeval delta;
848 gettimeofday (&now, 0);
850 delta.tv_sec = msecs/1000;
851 delta.tv_usec = (msecs - ((msecs/1000) * 1000)) * 1000;
853 timeradd (&now, &delta, &when);
855 _reset_display_at = (when.tv_sec * 1000000) + when.tv_usec;
859 Strip::clear_display_reset ()
861 _reset_display_at = 0;
865 Strip::reset_display ()
868 _surface->write (display (1, vpot_mode_string()));
870 _surface->write (blank_display (1));
873 clear_display_reset ();
876 struct RouteCompareByName {
877 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
878 return a->name().compare (b->name()) < 0;
883 Strip::maybe_add_to_bundle_map (BundleMap& bm, boost::shared_ptr<Bundle> b, bool for_input, const ChanCount& channels)
885 if (b->ports_are_outputs() == !for_input || b->nchannels() != channels) {
893 Strip::build_input_list (const ChanCount& channels)
895 boost::shared_ptr<ARDOUR::BundleList> b = _surface->mcp().get_session().bundles ();
897 input_bundles.clear ();
899 /* give user bundles first chance at being in the menu */
901 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
902 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
903 maybe_add_to_bundle_map (input_bundles, *i, true, channels);
907 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
908 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
909 maybe_add_to_bundle_map (input_bundles, *i, true, channels);
913 boost::shared_ptr<ARDOUR::RouteList> routes = _surface->mcp().get_session().get_routes ();
914 RouteList copy = *routes;
915 copy.sort (RouteCompareByName ());
917 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
918 maybe_add_to_bundle_map (input_bundles, (*i)->output()->bundle(), true, channels);
924 Strip::build_output_list (const ChanCount& channels)
926 boost::shared_ptr<ARDOUR::BundleList> b = _surface->mcp().get_session().bundles ();
928 output_bundles.clear ();
930 /* give user bundles first chance at being in the menu */
932 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
933 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
934 maybe_add_to_bundle_map (output_bundles, *i, false, channels);
938 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
939 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
940 maybe_add_to_bundle_map (output_bundles, *i, false, channels);
944 boost::shared_ptr<ARDOUR::RouteList> routes = _surface->mcp().get_session().get_routes ();
945 RouteList copy = *routes;
946 copy.sort (RouteCompareByName ());
948 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
949 maybe_add_to_bundle_map (output_bundles, (*i)->input()->bundle(), false, channels);
954 Strip::next_pot_mode ()
956 vector<PotMode>::iterator i;
958 if (_surface->mcp().flip_mode()) {
959 /* do not change vpot mode while in flipped mode */
960 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
961 _surface->write (display (1, "Flip"));
962 queue_display_reset (1000);
966 for (i = current_pot_modes.begin(); i != current_pot_modes.end(); ++i) {
967 if ((*i) == _vpot_mode) {
972 /* move to the next mode in the list, or back to the start (which will
973 also happen if the current mode is not in the current pot mode list)
976 if (i != current_pot_modes.end()) {
980 if (i == current_pot_modes.end()) {
981 i = current_pot_modes.begin();
988 Strip::set_vpot_mode (PotMode m)
990 boost::shared_ptr<Send> send;
991 boost::shared_ptr<Pannable> pannable;
993 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", m));
1001 /* one of these is unnecessary, but its not worth trying to find
1002 out which - it will just cause one additional message to be
1003 sent to the surface.
1006 _last_pan_position_written = -1;
1007 _last_gain_position_written = -1;
1009 switch (_vpot_mode) {
1013 pannable = _route->pannable ();
1015 if (_surface->mcp().flip_mode()) {
1016 /* gain to vpot, pan azi to fader */
1017 _vpot->set_control (_route->gain_control());
1019 _fader->set_control (pannable->pan_azimuth_control);
1023 /* gain to fader, pan azi to vpot */
1024 _fader->set_control (_route->gain_control());
1026 _vpot->set_control (pannable->pan_azimuth_control);
1032 pannable = _route->pannable ();
1034 if (_surface->mcp().flip_mode()) {
1035 /* gain to vpot, pan width to fader */
1036 _vpot->set_control (_route->gain_control());
1038 _fader->set_control (pannable->pan_width_control);
1042 /* gain to fader, pan width to vpot */
1043 _fader->set_control (_route->gain_control());
1045 _vpot->set_control (pannable->pan_width_control);
1061 send = boost::dynamic_pointer_cast<Send> (_route->nth_send (0));
1063 if (_surface->mcp().flip_mode()) {
1064 /* route gain to vpot, send gain to fader */
1065 _fader->set_control (send->amp()->gain_control());
1066 _vpot->set_control (_route->gain_control());
1069 /* route gain to fader, send gain to vpot */
1070 _vpot->set_control (send->amp()->gain_control());
1071 _fader->set_control (_route->gain_control());
1091 _surface->write (display (1, vpot_mode_string()));