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