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