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