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