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