MCP: fix handling of vpot presses
[ardour.git] / libs / surfaces / mackie / strip.cc
1 /*
2         Copyright (C) 2006,2007 John Anderson
3         Copyright (C) 2012 Paul Davis
4
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.
9
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.
14
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.
18 */
19
20 #include <sstream>
21 #include <stdint.h>
22 #include "strip.h"
23
24 #include <sys/time.h>
25
26 #include "midi++/port.h"
27
28 #include "pbd/compose.h"
29 #include "pbd/convert.h"
30
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"
45
46 #include "mackie_control_protocol.h"
47 #include "surface_port.h"
48 #include "surface.h"
49 #include "button.h"
50 #include "led.h"
51 #include "pot.h"
52 #include "fader.h"
53 #include "jog.h"
54 #include "meter.h"
55
56 using namespace Mackie;
57 using namespace std;
58 using namespace ARDOUR;
59 using namespace PBD;
60
61 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
62
63 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
64         : Group (name)
65         , _solo (0)
66         , _recenable (0)
67         , _mute (0)
68         , _select (0)
69         , _vselect (0)
70         , _fader_touch (0)
71         , _vpot (0)
72         , _fader (0)
73         , _index (index)
74         , _surface (&s)
75         , _controls_locked (false)
76         , _reset_display_at (0)
77         , _last_gain_position_written (-1.0)
78         , _last_pan_azi_position_written (-1.0)
79         , _last_pan_width_position_written (-1.0)
80 {
81         _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
82         _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
83         _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
84
85         for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
86                 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
87                 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
88                                                                    _surface->number(), index, Button::id_to_name (bb->bid()), 
89                                                                    bb->id(), b->second.base_id));
90         }
91 }       
92
93 Strip::~Strip ()
94 {
95         /* surface is responsible for deleting all controls */
96 }
97
98 void 
99 Strip::add (Control & control)
100 {
101         Button* button;
102
103         Group::add (control);
104
105         /* fader, vpot, meter were all set explicitly */
106
107         if ((button = dynamic_cast<Button*>(&control)) != 0) {
108                 switch (button->bid()) {
109                 case Button::RecEnable:
110                         _recenable = button;
111                         break;
112                 case Button::Mute:
113                         _mute = button;
114                         break;
115                 case Button::Solo:
116                         _solo = button;
117                         break;
118                 case Button::Select:
119                         _select = button;
120                         break;
121                 case Button::VSelect:
122                         _vselect = button;
123                         break;
124                 case Button::FaderTouch:
125                         _fader_touch = button;
126                 default:
127                         break;
128                 }
129         }
130 }
131
132 void
133 Strip::set_route (boost::shared_ptr<Route> r, bool with_messages)
134 {
135         if (_controls_locked) {
136                 return;
137         }
138
139         route_connections.drop_connections ();
140         
141         _solo->set_control (boost::shared_ptr<AutomationControl>());
142         _mute->set_control (boost::shared_ptr<AutomationControl>());
143         _select->set_control (boost::shared_ptr<AutomationControl>());
144         _recenable->set_control (boost::shared_ptr<AutomationControl>());
145         _fader->set_control (boost::shared_ptr<AutomationControl>());
146         _vpot->set_control (boost::shared_ptr<AutomationControl>());
147
148         _route = r;
149
150         control_by_parameter.clear ();
151         reset_saved_values ();
152
153         if (!r) {
154                 zero ();
155                 return;
156         }
157
158         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping route %3\n",
159                                                            _surface->number(), _index, _route->name()));
160         
161         _solo->set_control (_route->solo_control());
162         _mute->set_control (_route->mute_control());
163
164         set_vpot_parameter (PanAzimuthAutomation);
165         
166         _route->solo_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
167         _route->mute_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
168
169         boost::shared_ptr<Pannable> pannable = _route->pannable();
170
171         if (pannable && pannable->panner()) {
172                 pannable->pan_azimuth_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
173                 pannable->pan_width_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
174         }
175         _route->gain_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
176         _route->PropertyChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
177         
178         boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
179         
180         if (trk) {
181                 _recenable->set_control (trk->rec_enable_control());
182                 trk->rec_enable_control()->Changed .connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
183
184                 
185         }
186         
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.
189         
190         _route->active_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_active_changed, this), ui_context());
191         _route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_route_deleted, this), ui_context());
192         
193         /* Update */
194         
195         notify_all ();
196
197         /* setup legal VPot modes for this route */
198         
199         build_input_list (_route->input()->n_ports());
200         build_output_list (_route->output()->n_ports());
201
202         possible_pot_parameters.clear();
203
204         if (pannable) {
205                 boost::shared_ptr<Panner> panner = pannable->panner();
206                 if (panner) {
207                         set<Evoral::Parameter> automatable = panner->what_can_be_automated ();
208                         set<Evoral::Parameter>::iterator a;
209                         
210                         if ((a = automatable.find (PanAzimuthAutomation)) != automatable.end()) {
211                                 possible_pot_parameters.push_back (PanAzimuthAutomation);
212                         }
213                         
214                         if ((a = automatable.find (PanWidthAutomation)) != automatable.end()) {
215                                 possible_pot_parameters.push_back (PanWidthAutomation);
216                         }
217                 }
218         }
219 }
220
221 void 
222 Strip::notify_all()
223 {
224         if (!_route) {
225                 zero ();
226                 return;
227         }
228
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 ();
236 }
237
238 void 
239 Strip::notify_solo_changed ()
240 {
241         if (_route && _solo) {
242                 _surface->write (_solo->set_state (_route->soloed() ? on : off));
243         }
244 }
245
246 void 
247 Strip::notify_mute_changed ()
248 {
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)));
253
254                 _surface->write (_mute->set_state (_route->muted() ? on : off));
255         }
256 }
257
258 void 
259 Strip::notify_record_enable_changed ()
260 {
261         if (_route && _recenable)  {
262                 _surface->write (_recenable->set_state (_route->record_enabled() ? on : off));
263         }
264 }
265
266 void 
267 Strip::notify_active_changed ()
268 {
269         _surface->mcp().refresh_current_bank();
270 }
271
272 void 
273 Strip::notify_route_deleted ()
274 {
275         _surface->mcp().refresh_current_bank();
276 }
277
278 void 
279 Strip::notify_gain_changed (bool force_update)
280 {
281         if (_route) {
282                 
283                 Control* control;
284
285                 if (_surface->mcp().flip_mode()) {
286                         control = _vpot;
287                 } else {
288                         control = _fader;
289                 }
290
291
292                 boost::shared_ptr<AutomationControl> ac = _route->gain_control();
293                 
294                 float gain_coefficient = ac->get_value();
295                 float normalized_position = ac->internal_to_interface (gain_coefficient);
296                 
297                 if (force_update || normalized_position != _last_gain_position_written) {
298                         
299                         if (_surface->mcp().flip_mode()) {
300                                 if (!control->in_use()) {
301                                         _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
302                                 }
303                                 do_parameter_display (GainAutomation, gain_coefficient);
304                         } else {
305                                 if (!control->in_use()) {
306                                         _surface->write (_fader->set_position (normalized_position));
307                                 }
308                                 do_parameter_display (GainAutomation, gain_coefficient);
309                         }
310
311                         queue_display_reset (2000);
312                         _last_gain_position_written = normalized_position;
313                 }
314         }
315 }
316
317 void 
318 Strip::notify_property_changed (const PropertyChange& what_changed)
319 {
320         if (!what_changed.contains (ARDOUR::Properties::name)) {
321                 return;
322         }
323
324         if (_route) {
325                 string line1;
326                 string fullname = _route->name();
327                 
328                 if (fullname.length() <= 6) {
329                         line1 = fullname;
330                 } else {
331                         line1 = PBD::short_version (fullname, 6);
332                 }
333
334                 _surface->write (display (0, line1));
335         }
336 }
337
338 void 
339 Strip::notify_panner_azi_changed (bool force_update)
340 {
341         if (_route) {
342
343                 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
344
345                 boost::shared_ptr<Pannable> pannable = _route->pannable();
346
347                 if (!pannable || !pannable->panner()) {
348                         _surface->write (_vpot->zero());
349                         return;
350                 }
351
352                 Control* control = control_by_parameter[PanAzimuthAutomation];
353
354                 if (!control) {
355                         return;
356                 }
357
358                 double pos = pannable->pan_azimuth_control->internal_to_interface (pannable->pan_azimuth_control->get_value());
359                 
360                 if (force_update || pos != _last_pan_azi_position_written) {
361
362                         if (control == _fader) {
363                                 if (!_fader->in_use()) {
364                                         _surface->write (_fader->set_position (pos));
365                                 }
366                         } else if (control == _vpot) {
367                                 _surface->write (_vpot->set (pos, true, Pot::dot));
368                         }
369                         
370                         do_parameter_display (PanAzimuthAutomation, pos);
371                         queue_display_reset (2000);
372                         _last_pan_azi_position_written = pos;
373                 }
374         }
375 }
376
377 void 
378 Strip::notify_panner_width_changed (bool force_update)
379 {
380         if (_route) {
381
382                 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
383
384                 boost::shared_ptr<Pannable> pannable = _route->pannable();
385
386                 if (!pannable || !pannable->panner()) {
387                         _surface->write (_vpot->zero());
388                         return;
389                 }
390
391
392                 Control* control = control_by_parameter[PanWidthAutomation];
393
394                 if (!control) {
395                         return;
396                 }       
397                         
398                 double pos = pannable->pan_width_control->internal_to_interface (pannable->pan_width_control->get_value());
399                 
400                 if (force_update || pos != _last_pan_azi_position_written) {
401                         
402                         if (_surface->mcp().flip_mode()) {
403
404                                 if (control == _fader) {
405                                         if (!control->in_use()) {
406                                                 _surface->write (_fader->set_position (pos));
407                                         }
408                                 }
409
410                         } else if (control == _vpot) {
411                                 _surface->write (_vpot->set (pos, true, Pot::spread));
412                         }
413                         
414                         do_parameter_display (PanWidthAutomation, pos);
415                         queue_display_reset (2000);
416                         _last_pan_azi_position_written = pos;
417                 }
418         }
419 }
420
421 void
422 Strip::select_event (Button& button, ButtonState bs)
423 {
424         DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
425         
426         if (bs == press) {
427                 
428                 int ms = _surface->mcp().modifier_state();
429
430                 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
431                         _controls_locked = !_controls_locked;
432                         _surface->write (display (1,_controls_locked ?  "Locked" : "Unlock"));
433                         queue_display_reset (1000);
434                         return;
435                 }
436                 
437                 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
438                         /* reset to default */
439                         boost::shared_ptr<AutomationControl> ac = _fader->control ();
440                         if (ac) {
441                                 ac->set_value (ac->normal());
442                         }
443                         return;
444                 }
445                 
446                 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
447                 _surface->mcp().add_down_select_button (_surface->number(), _index);                    
448                 _surface->mcp().select_range ();
449                 
450         } else {
451                 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
452                 _surface->mcp().remove_down_select_button (_surface->number(), _index);                 
453         }
454 }
455
456 void
457 Strip::vselect_event (Button& button, ButtonState bs)
458 {
459         if (bs == press) {
460
461                 int ms = _surface->mcp().modifier_state();
462                                 
463                 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
464
465                         /* get the corresponding vpot and whatever its
466                          * controlling
467                          */
468
469                         Pot* pot = _surface->pots[Pot::ID + _index];
470
471                         if (pot) {
472                                 boost::shared_ptr<AutomationControl> ac = pot->control ();
473                                 
474                                 if (ac) {
475                                         
476                                         /* reset to default/normal value */
477                                         ac->set_value (ac->normal());
478                                 }
479                         }
480
481                 }  else {
482
483                         DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
484                         next_pot_mode ();
485                 }
486
487         }
488 }
489
490 void
491 Strip::fader_touch_event (Button& button, ButtonState bs)
492 {
493         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
494         
495         if (bs == press) {
496
497                 boost::shared_ptr<AutomationControl> ac = _fader->control ();
498
499                 if (_surface->mcp().modifier_state() == MackieControlProtocol::MODIFIER_SHIFT) {
500                         if (ac) {
501                                 ac->set_value (ac->normal());
502                         }
503                 } else {
504                 
505                         _fader->set_in_use (true);
506                         _fader->start_touch (_surface->mcp().transport_frame());
507                         
508                         if (ac) {
509                                 do_parameter_display ((AutomationType) ac->parameter().type(), ac->internal_to_interface (ac->get_value()));
510                                 queue_display_reset (2000);
511                         }
512                 }
513                         
514         } else {
515                 
516                 _fader->set_in_use (false);
517                 _fader->stop_touch (_surface->mcp().transport_frame(), true);
518                 
519         }
520 }       
521
522
523 void
524 Strip::handle_button (Button& button, ButtonState bs)
525 {
526         boost::shared_ptr<AutomationControl> control;
527
528         if (bs == press) {
529                 button.set_in_use (true);
530         } else {
531                 button.set_in_use (false);
532         }
533
534         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
535         
536         switch (button.bid()) {
537         case Button::Select:
538                 select_event (button, bs);
539                 break;
540                 
541         case Button::VSelect:
542                 vselect_event (button, bs);
543                 break;
544
545         case Button::FaderTouch:
546                 fader_touch_event (button, bs);
547                 break;
548
549         default:
550                 if ((control = button.control ())) {
551                         if (bs == press) {
552                                 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
553                                 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
554                                 
555                                 float new_value;
556                                 int ms = _surface->mcp().modifier_state();
557                                 
558                                 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
559                                         /* reset to default/normal value */
560                                         new_value = control->normal();
561                                 } else {
562                                         new_value = control->get_value() ? 0.0 : 1.0;
563                                 }
564                                 
565                                 /* get all controls that either have their
566                                  * button down or are within a range of
567                                  * several down buttons
568                                  */
569                                 
570                                 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type());
571                                 
572                                 
573                                 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
574                                                                             controls.size(), control->parameter().type(), new_value));
575
576                                 /* apply change */
577                                 
578                                 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
579                                         (*c)->set_value (new_value);
580                                 }
581                                 
582                         } else {
583                                 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
584                                 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
585                         }
586                 }
587                 break;
588         }
589 }
590
591 void
592 Strip::do_parameter_display (AutomationType type, float val)
593 {
594         switch (type) {
595         case GainAutomation:
596                 if (val == 0.0) {
597                         _surface->write (display (1, " -inf "));
598                 } else {
599                         char buf[16];
600                         float dB = accurate_coefficient_to_dB (val);
601                         snprintf (buf, sizeof (buf), "%6.1f", dB);
602                         _surface->write (display (1, buf));
603                 }               
604                 break;
605
606         case PanAzimuthAutomation:
607                 if (_route) {
608                         boost::shared_ptr<Pannable> p = _route->pannable();
609                         if (p && p->panner()) {
610                                 string str = p->panner()->value_as_string (p->pan_azimuth_control);
611                                 _surface->write (display (1, str));
612                         }
613                 }
614                 break;
615
616         case PanWidthAutomation:
617                 if (_route) {
618                         char buf[16];
619                         snprintf (buf, sizeof (buf), "%5ld%%", lrintf (val * 100.0));
620                         _surface->write (display (1, buf));
621                 }
622                 break;
623
624         default:
625                 break;
626         }
627 }
628
629 void
630 Strip::handle_fader (Fader& fader, float position)
631 {
632         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
633
634         fader.set_value (position);
635         fader.start_touch (_surface->mcp().transport_frame());
636         queue_display_reset (2000);
637
638         // must echo bytes back to slider now, because
639         // the notifier only works if the fader is not being
640         // touched. Which it is if we're getting input.
641
642         _surface->write (fader.set_position (position));
643 }
644
645 void
646 Strip::handle_pot (Pot& pot, float delta)
647 {
648         /* Pots only emit events when they move, not when they
649            stop moving. So to get a stop event, we need to use a timeout.
650         */
651         
652         boost::shared_ptr<AutomationControl> ac = pot.control();
653         double p = pot.get_value ();
654         p += delta;
655         p = max (ac->lower(), p);
656         p = min (ac->upper(), p);
657         pot.set_value (p);
658 }
659
660 void
661 Strip::periodic (uint64_t usecs)
662 {
663         if (!_route) {
664                 return;
665         }
666
667         update_automation ();
668         update_meter ();
669
670         if (_reset_display_at && _reset_display_at < usecs) {
671                 reset_display ();
672         }
673 }
674
675 void 
676 Strip::update_automation ()
677 {
678         ARDOUR::AutoState gain_state = _route->gain_control()->automation_state();
679
680         if (gain_state == Touch || gain_state == Play) {
681                 notify_gain_changed (false);
682         }
683
684         if (_route->panner()) {
685                 ARDOUR::AutoState panner_state = _route->panner()->automation_state();
686                 if (panner_state == Touch || panner_state == Play) {
687                         notify_panner_azi_changed (false);
688                         notify_panner_width_changed (false);
689                 }
690         }
691 }
692
693 void
694 Strip::update_meter ()
695 {
696         if (_meter) {
697                 float dB = const_cast<PeakMeter&> (_route->peak_meter()).peak_power (0);
698                 _surface->write (_meter->update_message (dB));
699         }
700 }
701
702 void
703 Strip::zero ()
704 {
705         for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
706                 _surface->write ((*it)->zero ());
707         }
708
709         _surface->write (blank_display (0));
710         _surface->write (blank_display (1));
711 }
712
713 MidiByteArray
714 Strip::blank_display (uint32_t line_number)
715 {
716         return display (line_number, string());
717 }
718
719 MidiByteArray
720 Strip::display (uint32_t line_number, const std::string& line)
721 {
722         assert (line_number <= 1);
723
724         MidiByteArray retval;
725
726         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
727
728         // sysex header
729         retval << _surface->sysex_hdr();
730         
731         // code for display
732         retval << 0x12;
733         // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
734         retval << (_index * 7 + (line_number * 0x38));
735         
736         // ascii data to display
737         retval << line;
738         // pad with " " out to 6 chars
739         for (int i = line.length(); i < 6; ++i) {
740                 retval << ' ';
741         }
742         
743         // column spacer, unless it's the right-hand column
744         if (_index < 7) {
745                 retval << ' ';
746         }
747
748         // sysex trailer
749         retval << MIDI::eox;
750         
751         return retval;
752 }
753
754 void
755 Strip::lock_controls ()
756 {
757         _controls_locked = true;
758 }
759
760 void
761 Strip::unlock_controls ()
762 {
763         _controls_locked = false;
764 }
765
766 void
767 Strip::gui_selection_changed (ARDOUR::RouteNotificationListPtr rl)
768 {
769         for (ARDOUR::RouteNotificationList::iterator i = rl->begin(); i != rl->end(); ++i) {
770                 if ((*i) == _route) {
771                         _surface->write (_select->set_state (on));
772                         return;
773                 }
774         }
775
776         _surface->write (_select->set_state (off));
777 }
778
779 string
780 Strip::vpot_mode_string () const
781 {
782         boost::shared_ptr<AutomationControl> ac = _vpot->control();
783         
784         if (!ac) {
785                 return string();
786         }
787
788         switch (ac->parameter().type()) {
789         case GainAutomation:
790                 return "Fader";
791         case PanAzimuthAutomation:
792                 return "Pan";
793         case PanWidthAutomation:
794                 return "Width";
795         case PanElevationAutomation:
796                 return "Elev";
797         case PanFrontBackAutomation:
798                 return "F/Rear";
799         case PanLFEAutomation:
800                 return "LFE";
801         }
802
803         return "???";
804 }
805
806 void
807 Strip::flip_mode_changed (bool notify)
808 {
809         if (!_route) {
810                 return;
811         }
812
813         reset_saved_values ();
814
815         boost::shared_ptr<AutomationControl> fader_controllable = _fader->control ();
816         boost::shared_ptr<AutomationControl> vpot_controllable = _vpot->control ();
817
818         _fader->set_control (vpot_controllable);
819         _vpot->set_control (fader_controllable);
820
821         control_by_parameter[fader_controllable->parameter()] = _vpot;
822         control_by_parameter[vpot_controllable->parameter()] = _fader;
823
824         _surface->write (display (1, vpot_mode_string ()));
825
826         if (notify) {
827                 notify_all ();
828         }
829 }
830
831 void
832 Strip::queue_display_reset (uint32_t msecs)
833 {
834         struct timeval now;
835         struct timeval delta;
836         struct timeval when;
837         gettimeofday (&now, 0);
838         
839         delta.tv_sec = msecs/1000;
840         delta.tv_usec = (msecs - ((msecs/1000) * 1000)) * 1000;
841         
842         timeradd (&now, &delta, &when);
843
844         _reset_display_at = (when.tv_sec * 1000000) + when.tv_usec;
845 }
846
847 void
848 Strip::clear_display_reset ()
849 {
850         _reset_display_at = 0;
851 }
852
853 void
854 Strip::reset_display ()
855 {
856         if (_route) {
857                 _surface->write (display (1, vpot_mode_string()));
858         } else {
859                 _surface->write (blank_display (1));
860         }
861                 
862         clear_display_reset ();
863 }
864                          
865 struct RouteCompareByName {
866         bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
867                 return a->name().compare (b->name()) < 0;
868         }
869 };
870
871 void
872 Strip::maybe_add_to_bundle_map (BundleMap& bm, boost::shared_ptr<Bundle> b, bool for_input, const ChanCount& channels)
873 {
874         if (b->ports_are_outputs() == !for_input  || b->nchannels() != channels) {
875                 return;
876         }
877
878         bm[b->name()] = b;
879 }
880
881 void
882 Strip::build_input_list (const ChanCount& channels)
883 {
884         boost::shared_ptr<ARDOUR::BundleList> b = _surface->mcp().get_session().bundles ();
885
886         input_bundles.clear ();
887
888         /* give user bundles first chance at being in the menu */
889         
890         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
891                 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
892                         maybe_add_to_bundle_map (input_bundles, *i, true, channels);
893                 }
894         }
895         
896         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
897                 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
898                         maybe_add_to_bundle_map (input_bundles, *i, true, channels);
899                 }
900         }
901         
902         boost::shared_ptr<ARDOUR::RouteList> routes = _surface->mcp().get_session().get_routes ();
903         RouteList copy = *routes;
904         copy.sort (RouteCompareByName ());
905
906         for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
907                 maybe_add_to_bundle_map (input_bundles, (*i)->output()->bundle(), true, channels);
908         }
909
910 }
911
912 void
913 Strip::build_output_list (const ChanCount& channels)
914 {
915         boost::shared_ptr<ARDOUR::BundleList> b = _surface->mcp().get_session().bundles ();
916
917         output_bundles.clear ();
918
919         /* give user bundles first chance at being in the menu */
920         
921         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
922                 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
923                         maybe_add_to_bundle_map (output_bundles, *i, false, channels);
924                 }
925         }
926         
927         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
928                 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
929                         maybe_add_to_bundle_map (output_bundles, *i, false, channels);
930                 }
931         }
932         
933         boost::shared_ptr<ARDOUR::RouteList> routes = _surface->mcp().get_session().get_routes ();
934         RouteList copy = *routes;
935         copy.sort (RouteCompareByName ());
936
937         for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
938                 maybe_add_to_bundle_map (output_bundles, (*i)->input()->bundle(), false, channels);
939         }
940 }
941
942 void
943 Strip::next_pot_mode ()
944 {
945         vector<Evoral::Parameter>::iterator i;
946
947         if (_surface->mcp().flip_mode()) {
948                 /* do not change vpot mode while in flipped mode */
949                 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
950                 _surface->write (display (1, "Flip"));
951                 queue_display_reset (1000);
952                 return;
953         }
954
955
956         boost::shared_ptr<AutomationControl> ac = _vpot->control();
957
958         if (!ac) {
959                 return;
960         }
961
962         if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter())) {
963                 return;
964         }
965
966         for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
967                 if ((*i) == ac->parameter()) {
968                         break;
969                 }
970         }
971
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)
974         */
975
976         if (i != possible_pot_parameters.end()) {
977                 ++i;
978         }
979
980         if (i == possible_pot_parameters.end()) {
981                 i = possible_pot_parameters.begin();
982         }
983
984         set_vpot_parameter (*i);
985 }
986
987 void
988 Strip::set_vpot_parameter (Evoral::Parameter p)
989 {
990         boost::shared_ptr<Pannable> pannable;
991
992         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
993
994         reset_saved_values ();
995
996         switch (p.type()) {
997         case PanAzimuthAutomation:
998                 pannable = _route->pannable ();
999                 if (pannable) {
1000                         if (_surface->mcp().flip_mode()) {
1001                                 /* gain to vpot, pan azi to fader */
1002                                 _vpot->set_control (_route->gain_control());
1003                                 control_by_parameter[GainAutomation] = _vpot;
1004                                 if (pannable) {
1005                                         _fader->set_control (pannable->pan_azimuth_control);
1006                                         control_by_parameter[PanAzimuthAutomation] = _fader;
1007                                 } else {
1008                                         _fader->set_control (boost::shared_ptr<AutomationControl>());
1009                                         control_by_parameter[PanAzimuthAutomation] = 0;
1010                                 }
1011                         } else {
1012                                 /* gain to fader, pan azi to vpot */
1013                                 _fader->set_control (_route->gain_control());
1014                                 control_by_parameter[GainAutomation] = _fader;
1015                                 if (pannable) {
1016                                         _vpot->set_control (pannable->pan_azimuth_control);
1017                                         control_by_parameter[PanAzimuthAutomation] = _vpot;
1018                                 } else {
1019                                         _vpot->set_control (boost::shared_ptr<AutomationControl>());
1020                                         control_by_parameter[PanAzimuthAutomation] = 0;
1021                                 }
1022                         }
1023                 }
1024                 break;
1025         case PanWidthAutomation:
1026                 pannable = _route->pannable ();
1027                 if (pannable) {
1028                         if (_surface->mcp().flip_mode()) {
1029                                 /* gain to vpot, pan width to fader */
1030                                 _vpot->set_control (_route->gain_control());
1031                                 control_by_parameter[GainAutomation] = _vpot;
1032                                 if (pannable) {
1033                                         _fader->set_control (pannable->pan_width_control);
1034                                         control_by_parameter[PanWidthAutomation] = _fader;
1035                                 } else {
1036                                         _fader->set_control (boost::shared_ptr<AutomationControl>());
1037                                         control_by_parameter[PanWidthAutomation] = 0;
1038                                 }
1039                         } else {
1040                                 /* gain to fader, pan width to vpot */
1041                                 _fader->set_control (_route->gain_control());
1042                                 control_by_parameter[GainAutomation] = _fader;
1043                                 if (pannable) {
1044                                         _vpot->set_control (pannable->pan_width_control);
1045                                         control_by_parameter[PanWidthAutomation] = _vpot;
1046                                 } else {
1047                                         _vpot->set_control (boost::shared_ptr<AutomationControl>());
1048                                         control_by_parameter[PanWidthAutomation] = 0;
1049                                 }
1050                         }
1051                 }
1052                 break;
1053         case PanElevationAutomation:
1054                 break;
1055         case PanFrontBackAutomation:
1056                 break;
1057         case PanLFEAutomation:
1058                 break;
1059         }
1060
1061         _surface->write (display (1, vpot_mode_string()));
1062 }
1063
1064 void
1065 Strip::reset_saved_values ()
1066 {
1067         _last_pan_azi_position_written = -1.0;
1068         _last_pan_width_position_written = -1.0;
1069         _last_gain_position_written = -1.0;
1070
1071 }