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