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