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