MCP: a fistful of improvements. probably best to just try it and see what it broken...
[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 #define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__))
63
64 extern PBD::EventLoop::InvalidationRecord* __invalidator (sigc::trackable& trackable, const char*, int);
65 #define invalidator() __invalidator (*(MackieControlProtocol::instance()), __FILE__, __LINE__)
66
67 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
68         : Group (name)
69         , _solo (0)
70         , _recenable (0)
71         , _mute (0)
72         , _select (0)
73         , _vselect (0)
74         , _fader_touch (0)
75         , _vpot (0)
76         , _vpot_mode (PanAzimuth)
77         , _preflip_vpot_mode (PanAzimuth)
78         , _fader (0)
79         , _index (index)
80         , _surface (&s)
81         , _controls_locked (false)
82         , _reset_display_at (0)
83         , _last_gain_position_written (-1.0)
84         , _last_pan_position_written (-1.0)
85 {
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));
89
90 #ifndef NDEBUG  
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));
96         }
97 #endif  
98 }       
99
100 Strip::~Strip ()
101 {
102         /* surface is responsible for deleting all controls */
103 }
104
105 void 
106 Strip::add (Control & control)
107 {
108         Button* button;
109
110         Group::add (control);
111
112         /* fader, vpot, meter were all set explicitly */
113
114         if ((button = dynamic_cast<Button*>(&control)) != 0) {
115                 switch (button->bid()) {
116                 case Button::RecEnable:
117                         _recenable = button;
118                         break;
119                 case Button::Mute:
120                         _mute = button;
121                         break;
122                 case Button::Solo:
123                         _solo = button;
124                         break;
125                 case Button::Select:
126                         _select = button;
127                         break;
128                 case Button::VSelect:
129                         _vselect = button;
130                         break;
131                 case Button::FaderTouch:
132                         _fader_touch = button;
133                 default:
134                         break;
135                 }
136         }
137 }
138
139 void
140 Strip::set_route (boost::shared_ptr<Route> r, bool with_messages)
141 {
142         if (_controls_locked) {
143                 return;
144         }
145
146         route_connections.drop_connections ();
147         
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>());
154
155         _route = r;
156
157         if (!r) {
158                 return;
159         }
160
161         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping route %3\n",
162                                                            _surface->number(), _index, _route->name()));
163         
164         _solo->set_control (_route->solo_control());
165         _mute->set_control (_route->mute_control());
166         set_vpot_mode (PanAzimuth);
167         
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());
170
171         boost::shared_ptr<Pannable> pannable = _route->pannable();
172
173         if (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());
176         }
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());
179         
180         boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
181         
182         if (trk) {
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());
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, 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());
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         current_pot_modes.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                                 current_pot_modes.push_back (PanAzimuth);
212                         }
213                         
214                         if ((a = automatable.find (PanWidthAutomation)) != automatable.end()) {
215                                 current_pot_modes.push_back (PanWidth);
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) {
348                         _surface->write (_vpot->zero());
349                         return;
350                 }
351
352                 Control* control;
353
354                 if (_surface->mcp().flip_mode()) {
355                         control = _fader;
356                 } else {
357                         if (_vpot_mode != PanAzimuth) {
358                                 return;
359                         }
360                         control = _vpot;
361                 }
362
363                 if (!control->in_use()) {
364                         
365                         double pos = pannable->pan_azimuth_control->internal_to_interface (pannable->pan_azimuth_control->get_value());
366                         
367                         if (force_update || pos != _last_pan_position_written) {
368                                 
369                                 if (_surface->mcp().flip_mode()) {
370                                         
371                                         _surface->write (_fader->set_position (pos));
372                                         do_parameter_display (PanAzimuthAutomation, pos);
373                                 } else {
374                                         _surface->write (_vpot->set (pos, true, Pot::dot));
375                                         do_parameter_display (PanAzimuthAutomation, pos);
376                                 }
377
378                                 queue_display_reset (2000);
379                                 _last_pan_position_written = pos;
380                         }
381                 }
382         }
383 }
384
385 void 
386 Strip::notify_panner_width_changed (bool force_update)
387 {
388         if (_route) {
389
390                 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
391
392                 boost::shared_ptr<Pannable> pannable = _route->pannable();
393
394                 if (!pannable) {
395                         _surface->write (_vpot->zero());
396                         return;
397                 }
398
399                 Control* control;
400
401                 if (_surface->mcp().flip_mode()) {
402                         control = _fader;
403                 } else {
404                         if (_vpot_mode != PanWidth) {
405                                 return;
406                         }
407                         control = _vpot;
408                 }
409
410                 if (!control->in_use()) {
411                         
412                         double pos = pannable->pan_width_control->internal_to_interface (pannable->pan_width_control->get_value());
413                         
414                         if (force_update || pos != _last_pan_position_written) {
415                                 
416                                 if (_surface->mcp().flip_mode()) {
417                                         
418                                         _surface->write (_fader->set_position (pos));
419                                         do_parameter_display (PanWidthAutomation, pos);
420                                 } else {
421                                         _surface->write (_vpot->set (pos, true, Pot::spread));
422                                         do_parameter_display (PanWidthAutomation, pos);
423                                 }
424
425                                 queue_display_reset (2000);
426                                 _last_pan_position_written = pos;
427                         }
428                 }
429         }
430 }
431
432 void
433 Strip::select_event (Button& button, ButtonState bs)
434 {
435         DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
436         
437         if (bs == press) {
438                 
439                 int ms = _surface->mcp().modifier_state();
440
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);
445                                 return;
446                 }
447                 
448                 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
449                         /* reset to default */
450                         boost::shared_ptr<AutomationControl> ac = _vpot->control ();
451                         if (ac) {
452                                 ac->set_value (ac->normal());
453                         }
454                         return;
455                 }
456                 
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 ();
460                 
461         } else {
462                 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
463                 _surface->mcp().remove_down_select_button (_surface->number(), _index);                 
464         }
465 }
466
467 void
468 Strip::vselect_event (Button& button, ButtonState bs)
469 {
470         if (bs == press) {
471
472
473                 int ms = _surface->mcp().modifier_state();
474                                 
475                 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
476                         boost::shared_ptr<AutomationControl> ac = button.control ();
477
478                         if (ac) {
479                                 
480                                 /* reset to default/normal value */
481                                 ac->set_value (ac->normal());
482                         }
483
484                 }  else {
485                         DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
486                         next_pot_mode ();
487                 }
488
489         }
490 }
491
492 void
493 Strip::fader_touch_event (Button& button, ButtonState bs)
494 {
495         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
496         
497         /* never use the modified control for fader stuff */
498         
499         if (bs == press) {
500                 
501                 _fader->set_in_use (true);
502                 _fader->start_touch (_surface->mcp().transport_frame());
503                 boost::shared_ptr<AutomationControl> ac = _fader->control ();
504                         if (ac) {
505                                 do_parameter_display ((AutomationType) ac->parameter().type(), ac->internal_to_interface (ac->get_value()));
506                                 queue_display_reset (2000);
507                         }
508                         
509         } else {
510                 
511                 _fader->set_in_use (false);
512                 _fader->stop_touch (_surface->mcp().transport_frame(), true);
513                 
514         }
515 }       
516
517
518 void
519 Strip::handle_button (Button& button, ButtonState bs)
520 {
521         boost::shared_ptr<AutomationControl> control;
522
523         if (bs == press) {
524                 button.set_in_use (true);
525         } else {
526                 button.set_in_use (false);
527         }
528
529         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
530         
531         switch (button.bid()) {
532         case Button::Select:
533                 select_event (button, bs);
534                 break;
535                 
536         case Button::VSelect:
537                 vselect_event (button, bs);
538                 break;
539
540         case Button::FaderTouch:
541                 fader_touch_event (button, bs);
542                 break;
543
544         default:
545                 if ((control = button.control ())) {
546                         if (bs == press) {
547                                 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
548                                 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
549                                 
550                                 float new_value;
551                                 int ms = _surface->mcp().modifier_state();
552                                 
553                                 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
554                                         /* reset to default/normal value */
555                                         new_value = control->normal();
556                                 } else {
557                                         new_value = control->get_value() ? 0.0 : 1.0;
558                                 }
559                                 
560                                 /* get all controls that either have their
561                                  * button down or are within a range of
562                                  * several down buttons
563                                  */
564                                 
565                                 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type());
566                                 
567                                 
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));
570
571                                 /* apply change */
572                                 
573                                 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
574                                         (*c)->set_value (new_value);
575                                 }
576                                 
577                         } else {
578                                 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
579                                 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
580                         }
581                 }
582                 break;
583         }
584 }
585
586 void
587 Strip::do_parameter_display (AutomationType type, float val)
588 {
589         switch (type) {
590         case GainAutomation:
591                 if (val == 0.0) {
592                         _surface->write (display (1, " -inf "));
593                 } else {
594                         char buf[16];
595                         float dB = accurate_coefficient_to_dB (val);
596                         snprintf (buf, sizeof (buf), "%6.1f", dB);
597                         _surface->write (display (1, buf));
598                 }               
599                 break;
600
601         case PanAzimuthAutomation:
602                 if (_route) {
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));
607                         }
608                 }
609                 break;
610
611         case PanWidthAutomation:
612                 if (_route) {
613                         char buf[16];
614                         snprintf (buf, sizeof (buf), "%5ld%%", lrintf (val * 100.0));
615                         _surface->write (display (1, buf));
616                 }
617                 break;
618
619         default:
620                 break;
621         }
622 }
623
624 void
625 Strip::handle_fader (Fader& fader, float position)
626 {
627         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
628
629         fader.set_value (position);
630         fader.start_touch (_surface->mcp().transport_frame());
631         queue_display_reset (2000);
632
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.
636
637         _surface->write (fader.set_position (position));
638 }
639
640 void
641 Strip::handle_pot (Pot& pot, float delta)
642 {
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.
645         */
646         
647         boost::shared_ptr<AutomationControl> ac = pot.control();
648         double p = pot.get_value ();
649         p += delta;
650         p = max (ac->lower(), p);
651         p = min (ac->upper(), p);
652         pot.set_value (p);
653 }
654
655 void
656 Strip::periodic (uint64_t usecs)
657 {
658         if (!_route) {
659                 return;
660         }
661
662         update_automation ();
663         update_meter ();
664
665         if (_reset_display_at && _reset_display_at < usecs) {
666                 reset_display ();
667         }
668 }
669
670 void 
671 Strip::update_automation ()
672 {
673         ARDOUR::AutoState gain_state = _route->gain_control()->automation_state();
674
675         if (gain_state == Touch || gain_state == Play) {
676                 notify_gain_changed (false);
677         }
678
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);
684                 }
685         }
686 }
687
688 void
689 Strip::update_meter ()
690 {
691         if (_meter) {
692                 float dB = const_cast<PeakMeter&> (_route->peak_meter()).peak_power (0);
693                 _surface->write (_meter->update_message (dB));
694         }
695 }
696
697 void
698 Strip::zero ()
699 {
700         for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
701                 _surface->write ((*it)->zero ());
702         }
703
704         _surface->write (blank_display (0));
705         _surface->write (blank_display (1));
706 }
707
708 MidiByteArray
709 Strip::blank_display (uint32_t line_number)
710 {
711         return display (line_number, string());
712 }
713
714 MidiByteArray
715 Strip::display (uint32_t line_number, const std::string& line)
716 {
717         assert (line_number <= 1);
718
719         MidiByteArray retval;
720
721         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
722
723         // sysex header
724         retval << _surface->sysex_hdr();
725         
726         // code for display
727         retval << 0x12;
728         // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
729         retval << (_index * 7 + (line_number * 0x38));
730         
731         // ascii data to display
732         retval << line;
733         // pad with " " out to 6 chars
734         for (int i = line.length(); i < 6; ++i) {
735                 retval << ' ';
736         }
737         
738         // column spacer, unless it's the right-hand column
739         if (_index < 7) {
740                 retval << ' ';
741         }
742
743         // sysex trailer
744         retval << MIDI::eox;
745         
746         return retval;
747 }
748
749 void
750 Strip::lock_controls ()
751 {
752         _controls_locked = true;
753 }
754
755 void
756 Strip::unlock_controls ()
757 {
758         _controls_locked = false;
759 }
760
761 MidiByteArray
762 Strip::gui_selection_changed (ARDOUR::RouteNotificationListPtr rl)
763 {
764         for (ARDOUR::RouteNotificationList::iterator i = rl->begin(); i != rl->end(); ++i) {
765                 if ((*i) == _route) {
766                         return _select->set_state (on);
767                 }
768         }
769
770         return _select->set_state (off);
771 }
772
773 string
774 Strip::vpot_mode_string () const
775 {
776         switch (_vpot_mode) {
777         case Gain:
778                 return "Fader";
779         case PanAzimuth:
780                 return "Pan";
781         case PanWidth:
782                 return "Width";
783         case PanElevation:
784                 return "Elev";
785         case PanFrontBack:
786                 return "F/Rear";
787         case PanLFE:
788                 return "LFE";
789         case Input:
790                 return "Input";
791         case Output:
792                 return "Output";
793         case Send1:
794                 return "Send 1";
795         case Send2:
796                 return "Send 2";
797         case Send3:
798                 return "Send 3";
799         case Send4:
800                 return "Send 4";
801         case Send5:
802                 return "Send 5";
803         case Send6:
804                 return "Send 6";
805         case Send7:
806                 return "Send 7";
807         case Send8:
808                 return "Send 8";
809         }
810
811         return "???";
812 }
813
814 void
815 Strip::flip_mode_changed (bool notify)
816 {
817         if (!_route) {
818                 return;
819         }
820
821         if (_surface->mcp().flip_mode()) {
822                 /* flip mode is on - save what it used to be */
823                 _preflip_vpot_mode = _vpot_mode;
824         } else {
825                 /* flip mode is off - restore flip mode to what it used to be */
826                 _vpot_mode = _preflip_vpot_mode;
827         }
828
829         boost::shared_ptr<AutomationControl> fader_controllable = _fader->control ();
830         boost::shared_ptr<AutomationControl> vpot_controllable = _vpot->control ();
831
832         _fader->set_control (vpot_controllable);
833         _vpot->set_control (fader_controllable);
834
835         _surface->write (display (1, vpot_mode_string ()));
836
837         if (notify) {
838                 notify_all ();
839         }
840 }
841
842 void
843 Strip::queue_display_reset (uint32_t msecs)
844 {
845         struct timeval now;
846         struct timeval delta;
847         struct timeval when;
848         gettimeofday (&now, 0);
849         
850         delta.tv_sec = msecs/1000;
851         delta.tv_usec = (msecs - ((msecs/1000) * 1000)) * 1000;
852         
853         timeradd (&now, &delta, &when);
854
855         _reset_display_at = (when.tv_sec * 1000000) + when.tv_usec;
856 }
857
858 void
859 Strip::clear_display_reset ()
860 {
861         _reset_display_at = 0;
862 }
863
864 void
865 Strip::reset_display ()
866 {
867         if (_route) {
868                 _surface->write (display (1, vpot_mode_string()));
869         } else {
870                 _surface->write (blank_display (1));
871         }
872                 
873         clear_display_reset ();
874 }
875                          
876 struct RouteCompareByName {
877         bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
878                 return a->name().compare (b->name()) < 0;
879         }
880 };
881
882 void
883 Strip::maybe_add_to_bundle_map (BundleMap& bm, boost::shared_ptr<Bundle> b, bool for_input, const ChanCount& channels)
884 {
885         if (b->ports_are_outputs() == !for_input  || b->nchannels() != channels) {
886                 return;
887         }
888
889         bm[b->name()] = b;
890 }
891
892 void
893 Strip::build_input_list (const ChanCount& channels)
894 {
895         boost::shared_ptr<ARDOUR::BundleList> b = _surface->mcp().get_session().bundles ();
896
897         input_bundles.clear ();
898
899         /* give user bundles first chance at being in the menu */
900         
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);
904                 }
905         }
906         
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);
910                 }
911         }
912         
913         boost::shared_ptr<ARDOUR::RouteList> routes = _surface->mcp().get_session().get_routes ();
914         RouteList copy = *routes;
915         copy.sort (RouteCompareByName ());
916
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);
919         }
920
921 }
922
923 void
924 Strip::build_output_list (const ChanCount& channels)
925 {
926         boost::shared_ptr<ARDOUR::BundleList> b = _surface->mcp().get_session().bundles ();
927
928         output_bundles.clear ();
929
930         /* give user bundles first chance at being in the menu */
931         
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);
935                 }
936         }
937         
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);
941                 }
942         }
943         
944         boost::shared_ptr<ARDOUR::RouteList> routes = _surface->mcp().get_session().get_routes ();
945         RouteList copy = *routes;
946         copy.sort (RouteCompareByName ());
947
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);
950         }
951 }
952
953 void
954 Strip::next_pot_mode ()
955 {
956         vector<PotMode>::iterator i;
957
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);
963                 return;
964         }
965
966         for (i = current_pot_modes.begin(); i != current_pot_modes.end(); ++i) {
967                 if ((*i) == _vpot_mode) {
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 != current_pot_modes.end()) {
977                 ++i;
978         }
979
980         if (i == current_pot_modes.end()) {
981                 i = current_pot_modes.begin();
982         }
983
984         set_vpot_mode (*i);
985 }
986
987 void
988 Strip::set_vpot_mode (PotMode m)
989 {
990         boost::shared_ptr<Send> send;
991         boost::shared_ptr<Pannable> pannable;
992
993         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", m));
994
995         if (!_route) {
996                 return;
997         }
998
999         _vpot_mode = m;
1000
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.
1004         */
1005
1006         _last_pan_position_written = -1;
1007         _last_gain_position_written = -1;
1008
1009         switch (_vpot_mode) {
1010         case Gain:
1011                 break;
1012         case PanAzimuth:
1013                 pannable = _route->pannable ();
1014                 if (pannable) {
1015                         if (_surface->mcp().flip_mode()) {
1016                                 /* gain to vpot, pan azi to fader */
1017                                 _vpot->set_control (_route->gain_control());
1018                                 if (pannable) {
1019                                         _fader->set_control (pannable->pan_azimuth_control);
1020                                 }
1021                                 _vpot_mode = Gain;
1022                         } else {
1023                                 /* gain to fader, pan azi to vpot */
1024                                 _fader->set_control (_route->gain_control());
1025                                 if (pannable) {
1026                                         _vpot->set_control (pannable->pan_azimuth_control);
1027                                 }
1028                         }
1029                 }
1030                 break;
1031         case PanWidth:
1032                 pannable = _route->pannable ();
1033                 if (pannable) {
1034                         if (_surface->mcp().flip_mode()) {
1035                                 /* gain to vpot, pan width to fader */
1036                                 _vpot->set_control (_route->gain_control());
1037                                 if (pannable) {
1038                                         _fader->set_control (pannable->pan_width_control);
1039                                 }
1040                                 _vpot_mode = Gain;
1041                         } else {
1042                                 /* gain to fader, pan width to vpot */
1043                                 _fader->set_control (_route->gain_control());
1044                                 if (pannable) {
1045                                         _vpot->set_control (pannable->pan_width_control);
1046                                 }
1047                         }
1048                 }
1049                 break;
1050         case PanElevation:
1051                 break;
1052         case PanFrontBack:
1053                 break;
1054         case PanLFE:
1055                 break;
1056         case Input:
1057                 break;
1058         case Output:
1059                 break;
1060         case Send1:
1061                 send = boost::dynamic_pointer_cast<Send> (_route->nth_send (0));
1062                 if (send) {
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());
1067                                 _vpot_mode = Gain;
1068                                 } else {
1069                                 /* route gain to fader, send gain to vpot */
1070                                 _vpot->set_control (send->amp()->gain_control());
1071                                 _fader->set_control (_route->gain_control());
1072                         }
1073                 }
1074                 break;
1075         case Send2:
1076                 break;
1077         case Send3:
1078                 break;
1079         case Send4:
1080                 break;
1081         case Send5:
1082                 break;
1083         case Send6:
1084                 break;
1085         case Send7:
1086                 break;
1087         case Send8:
1088                 break;
1089         };
1090
1091         _surface->write (display (1, vpot_mode_string()));
1092 }