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