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