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