57693d0e8f829e92d90fc6747b91aa59668044e6
[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                 boost::shared_ptr<AutomationControl> ac = button.control ();
444
445                 if (ac) {
446                         
447                         int ms = _surface->mcp().modifier_state();
448                         
449                         if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
450                                 /* reset to default/normal value */
451                                 ac->set_value (ac->normal());
452
453                         } else {
454                                 next_pot_mode ();
455                         }
456                 }
457         }
458 }
459
460 void
461 Strip::fader_touch_event (Button& button, ButtonState bs)
462 {
463         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
464         
465         /* never use the modified control for fader stuff */
466         
467         if (bs == press) {
468                 
469                 _fader->set_in_use (true);
470                 _fader->start_touch (_surface->mcp().transport_frame());
471                 boost::shared_ptr<AutomationControl> ac = _fader->control ();
472                         if (ac) {
473                                 do_parameter_display ((AutomationType) ac->parameter().type(), ac->internal_to_interface (ac->get_value()));
474                                 queue_display_reset (2000);
475                         }
476                         
477         } else {
478                 
479                 _fader->set_in_use (false);
480                 _fader->stop_touch (_surface->mcp().transport_frame(), true);
481                 
482         }
483 }       
484
485
486 void
487 Strip::handle_button (Button& button, ButtonState bs)
488 {
489         boost::shared_ptr<AutomationControl> control;
490
491         if (bs == press) {
492                 button.set_in_use (true);
493         } else {
494                 button.set_in_use (false);
495         }
496
497         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
498         
499         switch (button.bid()) {
500         case Button::Select:
501                 select_event (button, bs);
502                 break;
503                 
504         case Button::VSelect:
505                 vselect_event (button, bs);
506                 break;
507
508         case Button::FaderTouch:
509                 fader_touch_event (button, bs);
510                 break;
511
512         default:
513                 if ((control = button.control ())) {
514                         if (bs == press) {
515                                 DEBUG_TRACE (DEBUG::MackieControl, "add button on release\n");
516                                 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
517                                 
518                                 float new_value;
519                                 int ms = _surface->mcp().modifier_state();
520                                 
521                                 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
522                                         /* reset to default/normal value */
523                                         new_value = control->normal();
524                                 } else {
525                                         new_value = control->get_value() ? 0.0 : 1.0;
526                                 }
527                                 
528                                 /* get all controls that either have their
529                                  * button down or are within a range of
530                                  * several down buttons
531                                  */
532                                 
533                                 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type());
534                                 
535                                 
536                                 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
537                                                                             controls.size(), control->parameter().type(), new_value));
538
539                                 /* apply change */
540                                 
541                                 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
542                                         (*c)->set_value (new_value);
543                                 }
544                                 
545                         } else {
546                                 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
547                                 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
548                         }
549                 }
550                 break;
551         }
552 }
553
554 void
555 Strip::do_parameter_display (AutomationType type, float val)
556 {
557         float dB;
558
559         switch (type) {
560         case GainAutomation:
561                 dB = fast_coefficient_to_dB (val);
562                 if (val == 0.0) {
563                         _surface->write (display (1, " -inf "));
564                 } else {
565                         char buf[16];
566                         
567                         snprintf (buf, sizeof (buf), "%6.1f", dB);
568                         _surface->write (display (1, buf));
569                 }               
570                 break;
571
572         case PanAzimuthAutomation:
573                 if (_route) {
574                         boost::shared_ptr<Pannable> p = _route->pannable();
575                         if (p && p->panner()) {
576                                 string str = p->panner()->value_as_string (p->pan_azimuth_control);
577                                 _surface->write (display (1, str));
578                         }
579                 }
580         default:
581                 break;
582         }
583 }
584
585 void
586 Strip::handle_fader (Fader& fader, float position)
587 {
588         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
589
590         fader.set_value (position);
591         fader.start_touch (_surface->mcp().transport_frame());
592         queue_display_reset (2000);
593
594         // must echo bytes back to slider now, because
595         // the notifier only works if the fader is not being
596         // touched. Which it is if we're getting input.
597
598         _surface->write (fader.set_position (position));
599 }
600
601 void
602 Strip::handle_pot (Pot& pot, float delta)
603 {
604         /* Pots only emit events when they move, not when they
605            stop moving. So to get a stop event, we need to use a timeout.
606         */
607         
608         pot.start_touch (_surface->mcp().transport_frame());
609
610         double p = pot.get_value ();
611         p += delta;
612         p = min (1.0, p);
613         p = max (0.0, p);
614         pot.set_value (p);
615 }
616
617 void
618 Strip::periodic (uint64_t usecs)
619 {
620         if (!_route) {
621                 return;
622         }
623
624         update_automation ();
625         update_meter ();
626
627         if (_reset_display_at && _reset_display_at < usecs) {
628                 reset_display ();
629         }
630 }
631
632 void 
633 Strip::update_automation ()
634 {
635         ARDOUR::AutoState gain_state = _route->gain_control()->automation_state();
636
637         if (gain_state == Touch || gain_state == Play) {
638                 notify_gain_changed (false);
639         }
640
641         if (_route->panner()) {
642                 ARDOUR::AutoState panner_state = _route->panner()->automation_state();
643                 if (panner_state == Touch || panner_state == Play) {
644                         notify_panner_changed (false);
645                 }
646         }
647 }
648
649 void
650 Strip::update_meter ()
651 {
652         if (_meter) {
653                 float dB = const_cast<PeakMeter&> (_route->peak_meter()).peak_power (0);
654                 _surface->write (_meter->update_message (dB));
655         }
656 }
657
658 MidiByteArray
659 Strip::zero ()
660 {
661         MidiByteArray retval;
662
663         for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
664                 retval << (*it)->zero ();
665         }
666
667         retval << blank_display (0);
668         retval << blank_display (1);
669         
670         return retval;
671 }
672
673 MidiByteArray
674 Strip::blank_display (uint32_t line_number)
675 {
676         return display (line_number, string());
677 }
678
679 MidiByteArray
680 Strip::display (uint32_t line_number, const std::string& line)
681 {
682         assert (line_number <= 1);
683
684         MidiByteArray retval;
685
686         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
687
688         // sysex header
689         retval << _surface->sysex_hdr();
690         
691         // code for display
692         retval << 0x12;
693         // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
694         retval << (_index * 7 + (line_number * 0x38));
695         
696         // ascii data to display
697         retval << line;
698         // pad with " " out to 6 chars
699         for (int i = line.length(); i < 6; ++i) {
700                 retval << ' ';
701         }
702         
703         // column spacer, unless it's the right-hand column
704         if (_index < 7) {
705                 retval << ' ';
706         }
707
708         // sysex trailer
709         retval << MIDI::eox;
710         
711         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display midi: %1\n", retval));
712
713         return retval;
714 }
715
716 void
717 Strip::lock_controls ()
718 {
719         _controls_locked = true;
720 }
721
722 void
723 Strip::unlock_controls ()
724 {
725         _controls_locked = false;
726 }
727
728 MidiByteArray
729 Strip::gui_selection_changed (ARDOUR::RouteNotificationListPtr rl)
730 {
731         for (ARDOUR::RouteNotificationList::iterator i = rl->begin(); i != rl->end(); ++i) {
732                 if ((*i) == _route) {
733                         return _select->set_state (on);
734                 }
735         }
736
737         return _select->set_state (off);
738 }
739
740 string
741 Strip::vpot_mode_string () const
742 {
743         switch (_vpot_mode) {
744         case Gain:
745                 return "Fader";
746         case PanAzimuth:
747                 return "Pan";
748         case PanWidth:
749                 return "Width";
750         case PanElevation:
751                 return "Elev";
752         case PanFrontBack:
753                 return "F/Rear";
754         case PanLFE:
755                 return "LFE";
756         case Input:
757                 return "Input";
758         case Output:
759                 return "Output";
760         case Send1:
761                 return "Send 1";
762         case Send2:
763                 return "Send 2";
764         case Send3:
765                 return "Send 3";
766         case Send4:
767                 return "Send 4";
768         case Send5:
769                 return "Send 5";
770         case Send6:
771                 return "Send 6";
772         case Send7:
773                 return "Send 7";
774         case Send8:
775                 return "Send 8";
776         }
777
778         return "???";
779 }
780
781 void
782 Strip::flip_mode_changed (bool notify)
783 {
784         if (!_route) {
785                 return;
786         }
787
788         boost::shared_ptr<AutomationControl> fader_controllable = _fader->control ();
789         boost::shared_ptr<AutomationControl> vpot_controllable = _vpot->control ();
790
791         _fader->set_control (vpot_controllable);
792         _vpot->set_control (fader_controllable);
793
794         _surface->write (display (1, vpot_mode_string ()));
795
796         if (notify) {
797                 notify_all ();
798         }
799 }
800
801 void
802 Strip::queue_display_reset (uint32_t msecs)
803 {
804         struct timeval now;
805         struct timeval delta;
806         struct timeval when;
807         gettimeofday (&now, 0);
808         
809         delta.tv_sec = msecs/1000;
810         delta.tv_usec = (msecs - ((msecs/1000) * 1000)) * 1000;
811         
812         timeradd (&now, &delta, &when);
813
814         _reset_display_at = (when.tv_sec * 1000000) + when.tv_usec;
815 }
816
817 void
818 Strip::clear_display_reset ()
819 {
820         _reset_display_at = 0;
821 }
822
823 void
824 Strip::reset_display ()
825 {
826         _surface->write (display (1, vpot_mode_string()));
827         clear_display_reset ();
828 }
829                          
830 struct RouteCompareByName {
831         bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
832                 return a->name().compare (b->name()) < 0;
833         }
834 };
835
836 void
837 Strip::maybe_add_to_bundle_map (BundleMap& bm, boost::shared_ptr<Bundle> b, bool for_input, const ChanCount& channels)
838 {
839         if (b->ports_are_outputs() == !for_input  || b->nchannels() != channels) {
840                 return;
841         }
842
843         bm[b->name()] = b;
844 }
845
846 void
847 Strip::build_input_list (const ChanCount& channels)
848 {
849         boost::shared_ptr<ARDOUR::BundleList> b = _surface->mcp().get_session().bundles ();
850
851         input_bundles.clear ();
852
853         /* give user bundles first chance at being in the menu */
854         
855         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
856                 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
857                         maybe_add_to_bundle_map (input_bundles, *i, true, channels);
858                 }
859         }
860         
861         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
862                 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
863                         maybe_add_to_bundle_map (input_bundles, *i, true, channels);
864                 }
865         }
866         
867         boost::shared_ptr<ARDOUR::RouteList> routes = _surface->mcp().get_session().get_routes ();
868         RouteList copy = *routes;
869         copy.sort (RouteCompareByName ());
870
871         for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
872                 maybe_add_to_bundle_map (input_bundles, (*i)->output()->bundle(), true, channels);
873         }
874
875 }
876
877 void
878 Strip::build_output_list (const ChanCount& channels)
879 {
880         boost::shared_ptr<ARDOUR::BundleList> b = _surface->mcp().get_session().bundles ();
881
882         output_bundles.clear ();
883
884         /* give user bundles first chance at being in the menu */
885         
886         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
887                 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
888                         maybe_add_to_bundle_map (output_bundles, *i, false, channels);
889                 }
890         }
891         
892         for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
893                 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
894                         maybe_add_to_bundle_map (output_bundles, *i, false, channels);
895                 }
896         }
897         
898         boost::shared_ptr<ARDOUR::RouteList> routes = _surface->mcp().get_session().get_routes ();
899         RouteList copy = *routes;
900         copy.sort (RouteCompareByName ());
901
902         for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
903                 maybe_add_to_bundle_map (output_bundles, (*i)->input()->bundle(), false, channels);
904         }
905 }
906
907 void
908 Strip::next_pot_mode ()
909 {
910         vector<PotMode>::iterator i;
911
912         if (_surface->mcp().flip_mode()) {
913                 /* do not change vpot mode while in flipped mode */
914                 _surface->write (display (1, "Flip"));
915                 queue_display_reset (2000);
916                 return;
917         }
918
919         for (i = current_pot_modes.begin(); i != current_pot_modes.end(); ++i) {
920                 if ((*i) == _vpot_mode) {
921                         break;
922                 }
923         }
924
925         /* move to the next mode in the list, or back to the start (which will
926            also happen if the current mode is not in the current pot mode list)
927         */
928
929         if (i != current_pot_modes.end()) {
930                 ++i;
931         }
932
933         if (i == current_pot_modes.end()) {
934                 i = current_pot_modes.begin();
935         }
936
937         set_vpot_mode (*i);
938 }
939
940 void
941 Strip::set_vpot_mode (PotMode m)
942 {
943         boost::shared_ptr<Send> send;
944         boost::shared_ptr<Pannable> pannable;
945
946         if (!_route) {
947                 return;
948         }
949
950         _vpot_mode = m;
951
952         switch (_vpot_mode) {
953         case Gain:
954                 break;
955         case PanAzimuth:
956                 pannable = _route->pannable ();
957                 if (pannable) {
958                         if (_surface->mcp().flip_mode()) {
959                                 /* gain to vpot, pan azi to fader */
960                                 _vpot->set_control (_route->gain_control());
961                                 if (pannable) {
962                                         _fader->set_control (pannable->pan_azimuth_control);
963                                 }
964                                 _vpot_mode = Gain;
965                         } else {
966                                 /* gain to fader, pan azi to vpot */
967                                 _fader->set_control (_route->gain_control());
968                                 if (pannable) {
969                                         _vpot->set_control (pannable->pan_azimuth_control);
970                                 }
971                                 _vpot_mode = PanAzimuth;
972                         }
973                 }
974                 break;
975         case PanWidth:
976                 pannable = _route->pannable ();
977                 if (pannable) {
978                         if (_surface->mcp().flip_mode()) {
979                                 /* gain to vpot, pan width to fader */
980                                 _vpot->set_control (_route->gain_control());
981                                 if (pannable) {
982                                         _fader->set_control (pannable->pan_width_control);
983                                 }
984                                 _vpot_mode = Gain;
985                         } else {
986                                 /* gain to fader, pan width to vpot */
987                                 _fader->set_control (_route->gain_control());
988                                 if (pannable) {
989                                         _vpot->set_control (pannable->pan_width_control);
990                                 }
991                         }
992                 }
993                 break;
994         case PanElevation:
995                 break;
996         case PanFrontBack:
997                 break;
998         case PanLFE:
999                 break;
1000         case Input:
1001                 break;
1002         case Output:
1003                 break;
1004         case Send1:
1005                 send = boost::dynamic_pointer_cast<Send> (_route->nth_send (0));
1006                 if (send) {
1007                         if (_surface->mcp().flip_mode()) {
1008                                 /* route gain to vpot, send gain to fader */
1009                                 _fader->set_control (send->amp()->gain_control());
1010                                 _vpot->set_control (_route->gain_control());
1011                                 _vpot_mode = Gain;
1012                                 } else {
1013                                 /* route gain to fader, send gain to vpot */
1014                                 _vpot->set_control (send->amp()->gain_control());
1015                                 _fader->set_control (_route->gain_control());
1016                         }
1017                 }
1018                 break;
1019         case Send2:
1020                 break;
1021         case Send3:
1022                 break;
1023         case Send4:
1024                 break;
1025         case Send5:
1026                 break;
1027         case Send6:
1028                 break;
1029         case Send7:
1030                 break;
1031         case Send8:
1032                 break;
1033         };
1034
1035         _surface->write (display (1, vpot_mode_string()));
1036 }