FP8: don't allow to toggle Channelstrip bypass/enable
[ardour.git] / libs / surfaces / faderport8 / faderport8.cc
1 /*
2  * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
3  * Copyright (C) 2015 Paul Davis
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19
20 #include <cstdlib>
21 #include <sstream>
22 #include <algorithm>
23
24 #include <stdint.h>
25
26 #include "pbd/error.h"
27 #include "pbd/failed_constructor.h"
28 #include "pbd/pthread_utils.h"
29 #include "pbd/compose.h"
30 #include "pbd/xml++.h"
31
32 #include "midi++/port.h"
33
34 #include "ardour/audioengine.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/bundle.h"
37 #include "ardour/debug.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/midiport_manager.h"
40 #include "ardour/plugin_insert.h"
41 #include "ardour/processor.h"
42 #include "ardour/rc_configuration.h"
43 #include "ardour/route.h"
44 #include "ardour/session.h"
45 #include "ardour/session_configuration.h"
46 #include "ardour/vca.h"
47
48 #include "faderport8.h"
49
50 using namespace ARDOUR;
51 using namespace ArdourSurface;
52 using namespace PBD;
53 using namespace Glib;
54 using namespace std;
55 using namespace ArdourSurface::FP8Types;
56
57 #include "pbd/i18n.h"
58
59 #include "pbd/abstract_ui.cc" // instantiate template
60
61 #ifndef NDEBUG
62 //#define VERBOSE_DEBUG
63 #endif
64
65 static void
66 debug_2byte_msg (std::string const& msg, int b0, int b1)
67 {
68 #ifndef NDEBUG
69         if (DEBUG_ENABLED(DEBUG::FaderPort8)) {
70                 DEBUG_STR_DECL(a);
71                 DEBUG_STR_APPEND(a, "RECV: ");
72                 DEBUG_STR_APPEND(a, msg);
73                 DEBUG_STR_APPEND(a,' ');
74                 DEBUG_STR_APPEND(a,hex);
75                 DEBUG_STR_APPEND(a,"0x");
76                 DEBUG_STR_APPEND(a, b0);
77                 DEBUG_STR_APPEND(a,' ');
78                 DEBUG_STR_APPEND(a,"0x");
79                 DEBUG_STR_APPEND(a, b1);
80                 DEBUG_STR_APPEND(a,'\n');
81                 DEBUG_TRACE (DEBUG::FaderPort8, DEBUG_STR(a).str());
82         }
83 #endif
84 }
85
86 FaderPort8::FaderPort8 (Session& s)
87         : ControlProtocol (s, _("PreSonus FaderPort8"))
88         , AbstractUI<FaderPort8Request> (name())
89         , _connection_state (ConnectionState (0))
90         , _device_active (false)
91         , _ctrls (*this)
92         , _channel_off (0)
93         , _plugin_off (0)
94         , _parameter_off (0)
95         , _blink_onoff (false)
96         , _shift_lock (false)
97         , _shift_pressed (0)
98         , gui (0)
99 {
100         boost::shared_ptr<ARDOUR::Port> inp;
101         boost::shared_ptr<ARDOUR::Port> outp;
102
103         inp  = AudioEngine::instance()->register_input_port (DataType::MIDI, "FaderPort8 Recv", true);
104         outp = AudioEngine::instance()->register_output_port (DataType::MIDI, "FaderPort8 Send", true);
105         _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(inp);
106         _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(outp);
107
108         if (_input_port == 0 || _output_port == 0) {
109                 throw failed_constructor();
110         }
111
112         _input_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Receive)"), true));
113         _output_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Send) "), false));
114
115         _input_bundle->add_channel (
116                 inp->name(),
117                 ARDOUR::DataType::MIDI,
118                 session->engine().make_port_name_non_relative (inp->name())
119                 );
120
121         _output_bundle->add_channel (
122                 outp->name(),
123                 ARDOUR::DataType::MIDI,
124                 session->engine().make_port_name_non_relative (outp->name())
125                 );
126
127         ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::connection_handler, this, _2, _4), this);
128         ARDOUR::AudioEngine::instance()->Stopped.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::engine_reset, this), this);
129         ARDOUR::Port::PortDrop.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::engine_reset, this), this);
130
131         /* bind button events to call libardour actions */
132         setup_actions ();
133
134         _ctrls.FaderModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::notify_fader_mode_changed, this));
135         _ctrls.MixModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::assign_strips, this, true));
136 }
137
138 FaderPort8::~FaderPort8 ()
139 {
140         cerr << "~FP8\n";
141         disconnected ();
142         close ();
143
144         if (_input_port) {
145                 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
146                 AudioEngine::instance()->unregister_port (_input_port);
147                 _input_port.reset ();
148         }
149
150         if (_output_port) {
151                 _output_port->drain (10000,  250000); /* check every 10 msecs, wait up to 1/4 second for the port to drain */
152                 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering output port %1\n", boost::shared_ptr<ARDOUR::Port>(_output_port)->name()));
153                 AudioEngine::instance()->unregister_port (_output_port);
154                 _output_port.reset ();
155         }
156
157         tear_down_gui ();
158
159         /* stop event loop */
160         DEBUG_TRACE (DEBUG::FaderPort8, "BaseUI::quit ()\n");
161         BaseUI::quit ();
162 }
163
164 /* ****************************************************************************
165  * Event Loop
166  */
167
168 void*
169 FaderPort8::request_factory (uint32_t num_requests)
170 {
171         /* AbstractUI<T>::request_buffer_factory() is a template method only
172          * instantiated in this source module. To provide something visible for
173          * use in the interface/descriptor, we have this static method that is
174          * template-free.
175          */
176         return request_buffer_factory (num_requests);
177 }
178
179 void
180 FaderPort8::do_request (FaderPort8Request* req)
181 {
182         if (req->type == CallSlot) {
183                 call_slot (MISSING_INVALIDATOR, req->the_slot);
184         } else if (req->type == Quit) {
185                 stop ();
186         }
187 }
188
189 int
190 FaderPort8::stop ()
191 {
192         BaseUI::quit ();
193         return 0;
194 }
195
196 void
197 FaderPort8::thread_init ()
198 {
199         struct sched_param rtparam;
200
201         pthread_set_name (event_loop_name().c_str());
202
203         PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
204         ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
205
206         memset (&rtparam, 0, sizeof (rtparam));
207         rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
208
209         if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
210                 // do we care? not particularly.
211         }
212 }
213
214 bool
215 FaderPort8::periodic ()
216 {
217         /* prepare TC display -- handled by stripable Periodic () */
218         if (_ctrls.display_timecode ()) {
219                 // TODO allow BBT, HHMMSS
220                 // used in FP8Strip::periodic_update_timecode
221                 Timecode::Time TC;
222                 session->timecode_time (TC);
223                 _timecode = Timecode::timecode_format_time(TC);
224         } else {
225                 _timecode.clear ();
226         }
227
228         /* update stripables */
229         Periodic ();
230         return true;
231 }
232
233 bool
234 FaderPort8::blink_it ()
235 {
236         _blink_onoff = !_blink_onoff;
237         BlinkIt (_blink_onoff);
238         return true;
239 }
240
241 /* ****************************************************************************
242  * Port and Signal Connection Management
243  */
244 int
245 FaderPort8::set_active (bool yn)
246 {
247         DEBUG_TRACE (DEBUG::FaderPort8, string_compose("set_active init with yn: '%1'\n", yn));
248
249         if (yn == active()) {
250                 return 0;
251         }
252
253         if (yn) {
254                 /* start event loop */
255                 BaseUI::run ();
256                 connect_session_signals ();
257         } else {
258                 BaseUI::quit ();
259                 close ();
260         }
261
262         ControlProtocol::set_active (yn);
263         DEBUG_TRACE (DEBUG::FaderPort8, string_compose("set_active done with yn: '%1'\n", yn));
264         return 0;
265 }
266
267 void
268 FaderPort8::close ()
269 {
270         DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::close\n");
271         stop_midi_handling ();
272         session_connections.drop_connections ();
273         automation_state_connections.drop_connections ();
274         assigned_stripable_connections.drop_connections ();
275         _assigned_strips.clear ();
276         drop_ctrl_connections ();
277         port_connections.drop_connections ();
278         selection_connection.disconnect ();
279 }
280
281 void
282 FaderPort8::stop_midi_handling ()
283 {
284         _periodic_connection.disconnect ();
285         _blink_connection.disconnect ();
286         midi_connections.drop_connections ();
287         /* Note: the input handler is still active at this point, but we're no
288          * longer connected to any of the parser signals
289          */
290 }
291
292 void
293 FaderPort8::connected ()
294 {
295         DEBUG_TRACE (DEBUG::FaderPort8, "initializing\n");
296         assert (!_device_active);
297
298         if (_device_active) {
299                 stop_midi_handling (); // re-init
300         }
301
302         // ideally check firmware version >= 1.01 (USB bcdDevice 0x0101) (vendor 0x194f prod 0x0202)
303         // but we don't have a handle to the underlying USB device here.
304
305         _channel_off = _plugin_off = _parameter_off = 0;
306         _blink_onoff = false;
307         _shift_lock = false;
308         _shift_pressed = 0;
309
310         start_midi_handling ();
311         _ctrls.initialize ();
312
313         /* highlight bound user-actions */
314         for (FP8Controls::UserButtonMap::const_iterator i = _ctrls.user_buttons ().begin ();
315                         i != _ctrls.user_buttons ().end (); ++i) {
316                 _ctrls.button (i->first).set_active (! _user_action_map[i->first].empty ());
317         }
318         /* shift button lights */
319         tx_midi3 (0x90, 0x06, 0x00);
320         tx_midi3 (0x90, 0x46, 0x00);
321
322         send_session_state ();
323         assign_strips (true);
324
325         Glib::RefPtr<Glib::TimeoutSource> blink_timer =
326                 Glib::TimeoutSource::create (200);
327         _blink_connection = blink_timer->connect (sigc::mem_fun (*this, &FaderPort8::blink_it));
328         blink_timer->attach (main_loop()->get_context());
329
330         Glib::RefPtr<Glib::TimeoutSource> periodic_timer =
331                 Glib::TimeoutSource::create (100);
332         _periodic_connection = periodic_timer->connect (sigc::mem_fun (*this, &FaderPort8::periodic));
333         periodic_timer->attach (main_loop()->get_context());
334 }
335
336 void
337 FaderPort8::disconnected ()
338 {
339         stop_midi_handling ();
340         if (_device_active) {
341                 for (uint8_t id = 0; id < 8; ++id) {
342                         _ctrls.strip(id).unset_controllables ();
343                 }
344                 _ctrls.all_lights_off ();
345         }
346 }
347
348 void
349 FaderPort8::engine_reset ()
350 {
351         /* Port::PortDrop is called when the engine is halted or stopped */
352         DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::engine_reset\n");
353         _connection_state = 0;
354         _device_active = false;
355         disconnected ();
356 }
357
358 bool
359 FaderPort8::connection_handler (std::string name1, std::string name2)
360 {
361 #ifdef VERBOSE_DEBUG
362         DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::connection_handler: start\n");
363 #endif
364         if (!_input_port || !_output_port) {
365                 return false;
366         }
367
368         string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
369         string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
370
371         if (ni == name1 || ni == name2) {
372                 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connection notify %1 and %2\n", name1, name2));
373                 if (_input_port->connected ()) {
374                         if (_connection_state & InputConnected) {
375                                 return false;
376                         }
377                         _connection_state |= InputConnected;
378                 } else {
379                         _connection_state &= ~InputConnected;
380                 }
381         } else if (no == name1 || no == name2) {
382                 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connection notify %1 and %2\n", name1, name2));
383                 if (_output_port->connected ()) {
384                         if (_connection_state & OutputConnected) {
385                                 return false;
386                         }
387                         _connection_state |= OutputConnected;
388                 } else {
389                         _connection_state &= ~OutputConnected;
390                 }
391         } else {
392 #ifdef VERBOSE_DEBUG
393                 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
394 #endif
395                 /* not our ports */
396                 return false;
397         }
398
399         if ((_connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
400
401                 /* XXX this is a horrible hack. Without a short sleep here,
402                  * something prevents the device wakeup messages from being
403                  * sent and/or the responses from being received.
404                  */
405                 g_usleep (100000);
406                 DEBUG_TRACE (DEBUG::FaderPort8, "device now connected for both input and output\n");
407                 connected ();
408                 _device_active = true;
409
410         } else {
411                 DEBUG_TRACE (DEBUG::FaderPort8, "Device disconnected (input or output or both) or not yet fully connected\n");
412                 if (_device_active) {
413                         disconnected ();
414                 }
415                 _device_active = false;
416         }
417
418         ConnectionChange (); /* emit signal for our GUI */
419
420 #ifdef VERBOSE_DEBUG
421         DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::connection_handler: end\n");
422 #endif
423
424         return true; /* connection status changed */
425 }
426
427 list<boost::shared_ptr<ARDOUR::Bundle> >
428 FaderPort8::bundles ()
429 {
430         list<boost::shared_ptr<ARDOUR::Bundle> > b;
431
432         if (_input_bundle) {
433                 b.push_back (_input_bundle);
434                 b.push_back (_output_bundle);
435         }
436
437         return b;
438 }
439
440 /* ****************************************************************************
441  * MIDI I/O
442  */
443 bool
444 FaderPort8::midi_input_handler (Glib::IOCondition ioc, boost::weak_ptr<ARDOUR::AsyncMIDIPort> wport)
445 {
446         boost::shared_ptr<AsyncMIDIPort> port (wport.lock());
447
448         if (!port) {
449                 return false;
450         }
451
452 #ifdef VERBOSE_DEBUG
453         DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("something happend on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
454 #endif
455
456         if (ioc & ~IO_IN) {
457                 return false;
458         }
459
460         if (ioc & IO_IN) {
461
462                 port->clear ();
463 #ifdef VERBOSE_DEBUG
464                 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("data available on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
465 #endif
466                 framepos_t now = session->engine().sample_time();
467                 port->parse (now);
468         }
469
470         return true;
471 }
472
473 void
474 FaderPort8::start_midi_handling ()
475 {
476         _input_port->parser()->sysex.connect_same_thread (midi_connections, boost::bind (&FaderPort8::sysex_handler, this, _1, _2, _3));
477         _input_port->parser()->poly_pressure.connect_same_thread (midi_connections, boost::bind (&FaderPort8::polypressure_handler, this, _1, _2));
478         for (uint8_t i = 0; i < 16; ++i) {
479         _input_port->parser()->channel_pitchbend[i].connect_same_thread (midi_connections, boost::bind (&FaderPort8::pitchbend_handler, this, _1, i, _2));
480         }
481         _input_port->parser()->controller.connect_same_thread (midi_connections, boost::bind (&FaderPort8::controller_handler, this, _1, _2));
482         _input_port->parser()->note_on.connect_same_thread (midi_connections, boost::bind (&FaderPort8::note_on_handler, this, _1, _2));
483         _input_port->parser()->note_off.connect_same_thread (midi_connections, boost::bind (&FaderPort8::note_off_handler, this, _1, _2));
484
485         /* This connection means that whenever data is ready from the input
486          * port, the relevant thread will invoke our ::midi_input_handler()
487          * method, which will read the data, and invoke the parser.
488          */
489         _input_port->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &FaderPort8::midi_input_handler), boost::weak_ptr<AsyncMIDIPort> (_input_port)));
490         _input_port->xthread().attach (main_loop()->get_context());
491 }
492
493 size_t
494 FaderPort8::tx_midi (std::vector<uint8_t> const& d) const
495 {
496         /* work around midi buffer overflow for batch changes */
497         if (d.size() == 3 && (d[0] == 0x91 || d[0] == 0x92)) {
498                 /* set colors triplet in one go */
499         } else if (d.size() == 3 && (d[0] == 0x93)) {
500                 g_usleep (1500);
501         } else {
502                 g_usleep (400 * d.size());
503         }
504 #ifndef NDEBUG
505         size_t tx = _output_port->write (&d[0], d.size(), 0);
506         assert (tx == d.size());
507         return tx;
508 #else
509         return _output_port->write (&d[0], d.size(), 0);
510 #endif
511 }
512
513 /* ****************************************************************************
514  * MIDI Callbacks
515  */
516 void
517 FaderPort8::polypressure_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
518 {
519         debug_2byte_msg ("PP", tb->controller_number, tb->value);
520         // outgoing only (meter)
521 }
522
523 void
524 FaderPort8::pitchbend_handler (MIDI::Parser &, uint8_t chan, MIDI::pitchbend_t pb)
525 {
526         debug_2byte_msg ("PB", chan, pb);
527         /* fader 0..16368 (0x3ff0 -- 1024 steps) */
528         bool handled = _ctrls.midi_fader (chan, pb);
529         /* if Shift key is held while moving a fader (group override), don't lock shift. */
530         if ((_shift_pressed > 0) && handled) {
531                 _shift_connection.disconnect ();
532                 _shift_lock = false;
533         }
534 }
535
536 void
537 FaderPort8::controller_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
538 {
539         debug_2byte_msg ("CC", tb->controller_number, tb->value);
540         // encoder
541         // val Bit 7 = direction, Bits 0-6 = number of steps
542         if (tb->controller_number == 0x3c) {
543                 encoder_navigate (tb->value & 0x40 ? true : false, tb->value & 0x3f);
544         }
545         if (tb->controller_number == 0x10) {
546                 encoder_parameter (tb->value & 0x40 ? true : false, tb->value & 0x3f);
547         }
548 }
549
550 void
551 FaderPort8::note_on_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
552 {
553         debug_2byte_msg ("ON", tb->note_number, tb->velocity);
554
555         /* fader touch */
556         if (tb->note_number >= 0x68 && tb->note_number <= 0x6f) {
557                 _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
558                 return;
559         }
560
561         /* special case shift */
562         if (tb->note_number == 0x06 || tb->note_number == 0x46) {
563                 _shift_pressed |= (tb->note_number == 0x06) ? 1 : 2;
564                 if (_shift_pressed == 3) {
565                         return;
566                 }
567                 _shift_connection.disconnect ();
568                 if (_shift_lock) {
569                         _shift_lock = false;
570                         ShiftButtonChange (false);
571                         tx_midi3 (0x90, 0x06, 0x00);
572                         tx_midi3 (0x90, 0x46, 0x00);
573                         return;
574                 }
575
576                 Glib::RefPtr<Glib::TimeoutSource> shift_timer =
577                         Glib::TimeoutSource::create (1000);
578                 shift_timer->attach (main_loop()->get_context());
579                 _shift_connection = shift_timer->connect (sigc::mem_fun (*this, &FaderPort8::shift_timeout));
580
581                 ShiftButtonChange (true);
582                 tx_midi3 (0x90, 0x06, 0x7f);
583                 tx_midi3 (0x90, 0x46, 0x7f);
584                 return;
585         }
586
587         _ctrls.midi_event (tb->note_number, tb->velocity);
588 }
589
590 void
591 FaderPort8::note_off_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
592 {
593         debug_2byte_msg ("OF", tb->note_number, tb->velocity);
594
595         if (tb->note_number >= 0x68 && tb->note_number <= 0x6f) {
596                 // fader touch
597                 _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
598                 return;
599         }
600
601         /* special case shift */
602         if (tb->note_number == 0x06 || tb->note_number == 0x46) {
603                 _shift_pressed &= (tb->note_number == 0x06) ? 2 : 1;
604                 if (_shift_pressed > 0) {
605                         return;
606                 }
607                 if (_shift_lock) {
608                         return;
609                 }
610                 ShiftButtonChange (false);
611                 tx_midi3 (0x90, 0x06, 0x00);
612                 tx_midi3 (0x90, 0x46, 0x00);
613                 /* just in case this happens concurrently */
614                 _shift_connection.disconnect ();
615                 _shift_lock = false;
616                 return;
617         }
618
619         bool handled = _ctrls.midi_event (tb->note_number, tb->velocity);
620         /* if Shift key is held while activating an action, don't lock shift. */
621         if ((_shift_pressed > 0) && handled) {
622                 _shift_connection.disconnect ();
623                 _shift_lock = false;
624         }
625 }
626
627 void
628 FaderPort8::sysex_handler (MIDI::Parser &p, MIDI::byte *buf, size_t size)
629 {
630 #ifndef NDEBUG
631         if (DEBUG_ENABLED(DEBUG::FaderPort8)) {
632                 DEBUG_STR_DECL(a);
633                 DEBUG_STR_APPEND(a, string_compose ("RECV sysex siz=%1", size));
634                 for (size_t i=0; i < size; ++i) {
635                         DEBUG_STR_APPEND(a,hex);
636                         DEBUG_STR_APPEND(a,"0x");
637                         DEBUG_STR_APPEND(a,(int)buf[i]);
638                         DEBUG_STR_APPEND(a,' ');
639                 }
640                 DEBUG_STR_APPEND(a,'\n');
641                 DEBUG_TRACE (DEBUG::FaderPort8, DEBUG_STR(a).str());
642         }
643 #endif
644 }
645
646 /* ****************************************************************************
647  * User actions
648  */
649 void
650 FaderPort8::set_button_action (FP8Controls::ButtonId id, bool press, std::string const& action_name)
651 {
652         if (_ctrls.user_buttons().find (id) == _ctrls.user_buttons().end ()) {
653                 return;
654         }
655         _user_action_map[id].action (press).assign_action (action_name);
656
657         if (!_device_active) {
658                 return;
659         }
660         _ctrls.button (id).set_active (!_user_action_map[id].empty ());
661 }
662
663 std::string
664 FaderPort8::get_button_action (FP8Controls::ButtonId id, bool press)
665 {
666         return _user_action_map[id].action(press)._action_name;
667 }
668
669 /* ****************************************************************************
670  * Persistent State
671  */
672 XMLNode&
673 FaderPort8::get_state ()
674 {
675         DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::get_state\n");
676         XMLNode& node (ControlProtocol::get_state());
677
678         XMLNode* child;
679
680         child = new XMLNode (X_("Input"));
681         child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_input_port)->get_state());
682         node.add_child_nocopy (*child);
683
684         child = new XMLNode (X_("Output"));
685         child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_output_port)->get_state());
686         node.add_child_nocopy (*child);
687
688         for (UserActionMap::const_iterator i = _user_action_map.begin (); i != _user_action_map.end (); ++i) {
689                 if (i->second.empty()) {
690                         continue;
691                 }
692                 std::string name;
693                 if (!_ctrls.button_enum_to_name (i->first, name)) {
694                         continue;
695                 }
696                 XMLNode* btn = new XMLNode (X_("Button"));
697                 btn->set_property (X_("id"), name);
698                 if (!i->second.action(true).empty ()) {
699                         btn->set_property ("press", i->second.action(true)._action_name);
700                 }
701                 if (!i->second.action(false).empty ()) {
702                         btn->set_property ("release", i->second.action(false)._action_name);
703                 }
704                 node.add_child_nocopy (*btn);
705         }
706
707         return node;
708 }
709
710 int
711 FaderPort8::set_state (const XMLNode& node, int version)
712 {
713         DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state\n");
714         XMLNodeList nlist;
715         XMLNodeConstIterator niter;
716         XMLNode const* child;
717
718         if (ControlProtocol::set_state (node, version)) {
719                 return -1;
720         }
721
722         if ((child = node.child (X_("Input"))) != 0) {
723                 XMLNode* portnode = child->child (Port::state_node_name.c_str());
724                 if (portnode) {
725                         DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state Input\n");
726                         boost::shared_ptr<ARDOUR::Port>(_input_port)->set_state (*portnode, version);
727                 }
728         }
729
730         if ((child = node.child (X_("Output"))) != 0) {
731                 XMLNode* portnode = child->child (Port::state_node_name.c_str());
732                 if (portnode) {
733                         DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state Output\n");
734                         boost::shared_ptr<ARDOUR::Port>(_output_port)->set_state (*portnode, version);
735                 }
736         }
737
738         _user_action_map.clear ();
739         // TODO: When re-loading state w/o surface re-init becomes possible,
740         // unset lights and reset colors of user buttons.
741
742         for (XMLNodeList::const_iterator n = node.children().begin(); n != node.children().end(); ++n) {
743                 if ((*n)->name() != X_("Button")) {
744                         continue;
745                 }
746
747                 std::string id_str;
748                 if (!(*n)->get_property (X_("id"), id_str)) {
749                         continue;
750                 }
751
752                 FP8Controls::ButtonId id;
753                 if (!_ctrls.button_name_to_enum (id_str, id)) {
754                         continue;
755                 }
756
757                 std::string action_str;
758                 if ((*n)->get_property (X_("press"), action_str)) {
759                         set_button_action (id, true, action_str);
760                 }
761                 if ((*n)->get_property (X_("release"), action_str)) {
762                         set_button_action (id, false, action_str);
763                 }
764         }
765
766         return 0;
767 }
768
769 /* ****************************************************************************
770  * Stripable Assignment
771  */
772
773 static bool flt_audio_track (boost::shared_ptr<Stripable> s) {
774         return boost::dynamic_pointer_cast<AudioTrack>(s) != 0;
775 }
776
777 static bool flt_midi_track (boost::shared_ptr<Stripable> s) {
778         return boost::dynamic_pointer_cast<MidiTrack>(s) != 0;
779 }
780
781 static bool flt_bus (boost::shared_ptr<Stripable> s) {
782         if (boost::dynamic_pointer_cast<Route>(s) == 0) {
783                 return false;
784         }
785 #ifdef MIXBUS
786         if (s->mixbus () == 0) {
787                 return false;
788         }
789 #endif
790         return boost::dynamic_pointer_cast<Track>(s) == 0;
791 }
792
793 static bool flt_auxbus (boost::shared_ptr<Stripable> s) {
794         if (boost::dynamic_pointer_cast<Route>(s) == 0) {
795                 return false;
796         }
797 #ifdef MIXBUS
798         if (s->mixbus () > 0) {
799                 return false;
800         }
801 #endif
802         return boost::dynamic_pointer_cast<Track>(s) == 0;
803 }
804
805 static bool flt_vca (boost::shared_ptr<Stripable> s) {
806         return boost::dynamic_pointer_cast<VCA>(s) != 0;
807 }
808
809 static bool flt_selected (boost::shared_ptr<Stripable> s) {
810         return s->is_selected ();
811 }
812
813 static bool flt_mains (boost::shared_ptr<Stripable> s) {
814         return (s->is_master() || s->is_monitor());
815 }
816
817 static bool flt_all (boost::shared_ptr<Stripable> s) {
818         return true;
819 }
820
821 static bool flt_rec_armed (boost::shared_ptr<Stripable> s) {
822         boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
823         if (!t) {
824                 return false;
825         }
826         return t->rec_enable_control ()->get_value () > 0.;
827 }
828
829 static bool flt_instrument (boost::shared_ptr<Stripable> s) {
830         boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(s);
831         if (!r) {
832                 return false;
833         }
834         return 0 != r->the_instrument ();
835 }
836
837 void
838 FaderPort8::filter_stripables (StripableList& strips) const
839 {
840         typedef bool (*FilterFunction)(boost::shared_ptr<Stripable>);
841         FilterFunction flt;
842
843         bool allow_master = false;
844         bool allow_monitor = false;
845
846         switch (_ctrls.mix_mode ()) {
847                 case MixAudio:
848                         flt = &flt_audio_track;
849                         break;
850                 case MixInstrument:
851                         flt = &flt_instrument;
852                         break;
853                 case MixBus:
854                         flt = &flt_bus;
855                         break;
856                 case MixVCA:
857                         flt = &flt_vca;
858                         break;
859                 case MixMIDI:
860                         flt = &flt_midi_track;
861                         break;
862                 case MixUser:
863                         allow_master = true;
864                         flt = &flt_selected;
865                         break;
866                 case MixOutputs:
867                         allow_master = true;
868                         allow_monitor = true;
869                         flt = &flt_mains;
870                         break;
871                 case MixInputs:
872                         flt = &flt_rec_armed;
873                         break;
874                 case MixFX:
875                         flt = &flt_auxbus;
876                         break;
877                 default:
878                         assert (0);
879                         // fall through
880                 case MixAll:
881                         allow_master = true;
882                         flt = &flt_all;
883                         break;
884         }
885
886         StripableList all;
887         session->get_stripables (all);
888
889         for (StripableList::const_iterator s = all.begin(); s != all.end(); ++s) {
890                 if ((*s)->is_auditioner ()) { continue; }
891                 if ((*s)->is_hidden ()) { continue; }
892
893                 if (!allow_master  && (*s)->is_master ()) { continue; }
894                 if (!allow_monitor && (*s)->is_monitor ()) { continue; }
895
896                 if ((*flt)(*s)) {
897                         strips.push_back (*s);
898                 }
899         }
900         strips.sort (Stripable::Sorter());
901 }
902
903 /* Track/Pan mode: assign stripable to strips */
904 void
905 FaderPort8::assign_stripables (bool select_only)
906 {
907         StripableList strips;
908         filter_stripables (strips);
909
910         if (!select_only) {
911                 set_periodic_display_mode (FP8Strip::Stripables);
912         }
913
914         int n_strips = strips.size();
915         _channel_off = std::min (_channel_off, n_strips - 8);
916         _channel_off = std::max (0, _channel_off);
917
918         uint8_t id = 0;
919         int skip = _channel_off;
920         for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
921                 if (skip > 0) {
922                         --skip;
923                         continue;
924                 }
925
926                 _assigned_strips[*s] = id;
927                 (*s)->DropReferences.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
928                                 boost::bind (&FaderPort8::notify_stripable_added_or_removed, this), this);
929
930                 (*s)->PropertyChanged.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
931                                 boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
932                 (*s)->presentation_info ().PropertyChanged.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
933                                 boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
934
935                 if (select_only) {
936                         _ctrls.strip(id).set_text_line (3, (*s)->name (), true);
937                         _ctrls.strip(id).select_button ().set_color ((*s)->presentation_info ().color());
938                         /* update selection lights */
939                         _ctrls.strip(id).select_button ().set_active ((*s)->is_selected ());
940                         _ctrls.strip(id).select_button ().set_blinking (*s == first_selected_stripable ());
941                 } else {
942                         _ctrls.strip(id).set_stripable (*s, _ctrls.fader_mode() == ModePan);
943                 }
944
945                  boost::function<void ()> cb (boost::bind (&FaderPort8::select_strip, this, boost::weak_ptr<Stripable> (*s)));
946                  _ctrls.strip(id).set_select_cb (cb);
947
948                 if (++id == 8) {
949                         break;
950                 }
951         }
952         for (; id < 8; ++id) {
953                 _ctrls.strip(id).unset_controllables (select_only ? (FP8Strip::CTRL_SELECT | FP8Strip::CTRL_TEXT3) : FP8Strip::CTRL_ALL);
954         }
955 }
956
957 /* ****************************************************************************
958  * Plugin selection and parameters
959  */
960
961 void
962 FaderPort8::assign_processor_ctrls ()
963 {
964         if (_proc_params.size() == 0) {
965                 _ctrls.set_fader_mode (ModeTrack);
966                 return;
967         }
968         set_periodic_display_mode (FP8Strip::PluginParam);
969
970         std::vector <ProcessorCtrl*> toggle_params;
971         std::vector <ProcessorCtrl*> slider_params;
972
973         for ( std::list <ProcessorCtrl>::iterator i = _proc_params.begin(); i != _proc_params.end(); ++i) {
974                 if ((*i).ac->toggled()) {
975                         toggle_params.push_back (&(*i));
976                 } else {
977                         slider_params.push_back (&(*i));
978                 }
979         }
980
981         int n_parameters = std::max (toggle_params.size(), slider_params.size());
982
983         _parameter_off = std::min (_parameter_off, n_parameters - 8);
984         _parameter_off = std::max (0, _parameter_off);
985
986         uint8_t id = 0;
987         for (size_t i = _parameter_off; i < (size_t)n_parameters; ++i) {
988                 if (i >= toggle_params.size ()) {
989                         _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT1 & ~FP8Strip::CTRL_TEXT2);
990                 }
991                 else if (i >= slider_params.size ()) {
992                         _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_SELECT & ~FP8Strip::CTRL_TEXT3);
993                 } else {
994                         _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
995                 }
996
997                 if (i < slider_params.size ()) {
998                         _ctrls.strip(id).set_fader_controllable (slider_params[i]->ac);
999                         std::string param_name = slider_params[i]->name;
1000                         _ctrls.strip(id).set_text_line (0, param_name.substr (0, 9));
1001                         _ctrls.strip(id).set_text_line (1, param_name.length () > 9 ? param_name.substr (9) : "");
1002                 }
1003                 if (i < toggle_params.size ()) {
1004                         _ctrls.strip(id).set_select_controllable (toggle_params[i]->ac);
1005                         _ctrls.strip(id).set_text_line (3, toggle_params[i]->name, true);
1006                 }
1007                  if (++id == 8) {
1008                          break;
1009                  }
1010         }
1011
1012         // clear remaining
1013         for (; id < 8; ++id) {
1014                 _ctrls.strip(id).unset_controllables ();
1015         }
1016 }
1017
1018 void
1019 FaderPort8::build_well_known_processor_ctrls (boost::shared_ptr<Stripable> s, bool eq)
1020 {
1021 #define PUSH_BACK_NON_NULL(N, C) do {if (C) { _proc_params.push_back (ProcessorCtrl (N, C)); }} while (0)
1022
1023         _proc_params.clear ();
1024         if (eq) {
1025                 int cnt = s->eq_band_cnt();
1026
1027 #ifdef MIXBUS32C
1028                 PUSH_BACK_NON_NULL ("Flt In", s->filter_enable_controllable (true)); // both HP/LP
1029                 PUSH_BACK_NON_NULL ("HP Freq", s->filter_freq_controllable (true));
1030                 PUSH_BACK_NON_NULL ("LP Freq", s->filter_freq_controllable (false));
1031                 PUSH_BACK_NON_NULL ("EQ In", s->eq_enable_controllable ());
1032 #elif defined (MIXBUS)
1033                 PUSH_BACK_NON_NULL ("EQ In", s->eq_enable_controllable ());
1034                 PUSH_BACK_NON_NULL ("HP Freq", s->filter_freq_controllable (true));
1035 #endif
1036
1037                 for (int band = 0; band < cnt; ++band) {
1038                         std::string bn = s->eq_band_name (band);
1039                         PUSH_BACK_NON_NULL (string_compose ("Gain %1", bn), s->eq_gain_controllable (band));
1040                         PUSH_BACK_NON_NULL (string_compose ("Freq %1", bn), s->eq_freq_controllable (band));
1041                         PUSH_BACK_NON_NULL (string_compose ("Band %1", bn), s->eq_q_controllable (band));
1042                         PUSH_BACK_NON_NULL (string_compose ("Shape %1", bn), s->eq_shape_controllable (band));
1043                 }
1044         } else {
1045                 PUSH_BACK_NON_NULL ("Comp In", s->comp_enable_controllable ());
1046                 PUSH_BACK_NON_NULL ("Threshold", s->comp_threshold_controllable ());
1047                 PUSH_BACK_NON_NULL ("Speed", s->comp_speed_controllable ());
1048                 PUSH_BACK_NON_NULL ("Mode", s->comp_mode_controllable ());
1049         }
1050 }
1051
1052 void
1053 FaderPort8::select_plugin (int num)
1054 {
1055         // make sure drop_ctrl_connections() was called
1056         assert (_proc_params.size() == 0 && _showing_well_known == 0 && _plugin_insert.expired());
1057
1058         boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (first_selected_stripable());
1059         if (!r) {
1060                 _ctrls.set_fader_mode (ModeTrack);
1061                 return;
1062         }
1063
1064         // Toggle Bypass
1065         if (shift_mod ()) {
1066                 if (num >= 0) {
1067                         boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (r->nth_plugin (num));
1068 #ifdef MIXBUS
1069                         if (pi && !pi->is_channelstrip () && pi->display_to_user ())
1070 #else
1071                         if (pi && pi->display_to_user ())
1072 #endif
1073                         {
1074                                 pi->enable (! pi->enabled ());
1075                         }
1076                 }
1077                 return;
1078         }
1079
1080         if (num < 0) {
1081                 build_well_known_processor_ctrls (r, num == -1);
1082                 assign_processor_ctrls ();
1083                 _showing_well_known = num;
1084                 return;
1085         }
1086         _showing_well_known = 0;
1087
1088         boost::shared_ptr<Processor> proc = r->nth_plugin (num);
1089         if (!proc) {
1090                 _ctrls.set_fader_mode (ModeTrack);
1091                 return;
1092         }
1093
1094         // disconnect signals from spill_plugins: processors_changed and ActiveChanged
1095         processor_connections.drop_connections ();
1096         r->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1097
1098         boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1099         assert (pi); // nth_plugin() always returns a PI.
1100         /* _plugin_insert is used for Bypass/Enable */
1101 #ifdef MIXBUS
1102         if (!pi->is_channelstrip () && pi->display_to_user ())
1103 #else
1104         if (pi->display_to_user ())
1105 #endif
1106         {
1107                 _plugin_insert = boost::weak_ptr<ARDOUR::PluginInsert> (pi);
1108                 pi->ActiveChanged.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_plugin_active_changed, this), this);
1109         }
1110
1111         // switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed()
1112         // which drops the references, disconnects the signal and re-spills tracks
1113         proc->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1114
1115         // build params
1116         _proc_params.clear();
1117         set<Evoral::Parameter> p = proc->what_can_be_automated ();
1118         for (set<Evoral::Parameter>::iterator i = p.begin(); i != p.end(); ++i) {
1119                 std::string n = proc->describe_parameter (*i);
1120                 if (n == "hidden") {
1121                         continue;
1122                 }
1123                 _proc_params.push_back (ProcessorCtrl (n, proc->automation_control (*i)));
1124         }
1125
1126         // TODO: open plugin GUI  if (_proc_params.size() > 0)
1127
1128         // display
1129         assign_processor_ctrls ();
1130         notify_plugin_active_changed ();
1131 }
1132
1133 /* short 4 chars at most */
1134 static std::string plugintype (ARDOUR::PluginType t) {
1135         switch (t) {
1136                 case AudioUnit:
1137                         return "AU";
1138                 case LADSPA:
1139                         return "LV1";
1140                 case LV2:
1141                         return "LV2";
1142                 case Windows_VST:
1143                 case LXVST:
1144                 case MacVST:
1145                         return "VST";
1146                 case Lua:
1147                         return "Lua";
1148                 default:
1149                         break;
1150         }
1151         return enum_2_string (t);
1152 }
1153
1154 void
1155 FaderPort8::spill_plugins ()
1156 {
1157         boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (first_selected_stripable());
1158         if (!r) {
1159                 _ctrls.set_fader_mode (ModeTrack);
1160                 return;
1161         }
1162
1163         drop_ctrl_connections ();
1164
1165         // switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed()
1166         // which drops the references, disconnects the signal and re-spills tracks
1167         r->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1168
1169         // update when processor change
1170         r->processors_changed.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::spill_plugins, this), this);
1171
1172         // count available
1173         boost::shared_ptr<Processor> proc;
1174
1175         std::vector<uint32_t> procs;
1176
1177         for (uint32_t i = 0; 0 != (proc = r->nth_plugin (i)); ++i) {
1178                 if (!proc->display_to_user ()) {
1179 #ifdef MIXBUS
1180                         boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1181                         if (pi->is_channelstrip ()) // don't skip MB PRE
1182 #endif
1183                         continue;
1184                 }
1185                 int n_controls = 0;
1186                 set<Evoral::Parameter> p = proc->what_can_be_automated ();
1187                 for (set<Evoral::Parameter>::iterator j = p.begin(); j != p.end(); ++j) {
1188                         std::string n = proc->describe_parameter (*j);
1189                         if (n == "hidden") {
1190                                 continue;
1191                         }
1192                         ++n_controls;
1193                 }
1194                 if (n_controls > 0) {
1195                         procs.push_back (i);
1196                 }
1197         }
1198
1199         int n_plugins = procs.size();
1200         int spillwidth = 8;
1201         bool have_well_known_eq = false;
1202         bool have_well_known_comp = false;
1203
1204         // reserve last slot(s) for "well-known"
1205         if (r->eq_band_cnt() > 0) {
1206                 --spillwidth;
1207                 have_well_known_eq = true;
1208         }
1209         if (r->comp_enable_controllable ()) {
1210                 --spillwidth;
1211                 have_well_known_comp = true;
1212         }
1213
1214         if (n_plugins == 0 && !have_well_known_eq && !have_well_known_comp) {
1215                 _ctrls.set_fader_mode (ModeTrack);
1216                 return;
1217         }
1218
1219         set_periodic_display_mode (FP8Strip::PluginSelect);
1220
1221         _plugin_off = std::min (_plugin_off, n_plugins - spillwidth);
1222         _plugin_off = std::max (0, _plugin_off);
1223
1224         uint8_t id = 0;
1225         for (uint32_t i = _plugin_off; ; ++i) {
1226                 if (i >= procs.size()) {
1227                         break;
1228                 }
1229                 boost::shared_ptr<Processor> proc = r->nth_plugin (procs[i]);
1230                 if (!proc) {
1231                         break;
1232                 }
1233                 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1234                 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, procs[i]));
1235
1236                 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1237                 _ctrls.strip(id).set_select_cb (cb);
1238                 _ctrls.strip(id).select_button ().set_color (proc->enabled () ? 0x00ff00ff : 0xff0000ff);
1239                 _ctrls.strip(id).select_button ().set_active (true);
1240                 _ctrls.strip(id).select_button ().set_blinking (false);
1241                 _ctrls.strip(id).set_text_line (0, proc->name());
1242                 _ctrls.strip(id).set_text_line (1, pi->plugin()->maker());
1243                 _ctrls.strip(id).set_text_line (2, plugintype (pi->type()));
1244                 _ctrls.strip(id).set_text_line (3, "");
1245
1246                 pi->ActiveChanged.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::spill_plugins, this), this);
1247
1248                 if (++id == spillwidth) {
1249                         break;
1250                 }
1251         }
1252         // clear remaining
1253         for (; id < spillwidth; ++id) {
1254                 _ctrls.strip(id).unset_controllables ();
1255         }
1256
1257         if (have_well_known_comp) {
1258                         assert (id < 8);
1259                  boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -2));
1260                  _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1261                  _ctrls.strip(id).set_select_cb (cb);
1262                  _ctrls.strip(id).select_button ().set_color (0xffff00ff);
1263                  _ctrls.strip(id).select_button ().set_active (true);
1264                  _ctrls.strip(id).select_button ().set_blinking (false);
1265                  _ctrls.strip(id).set_text_line (0, "Comp");
1266                  _ctrls.strip(id).set_text_line (1, "Built-In");
1267                  _ctrls.strip(id).set_text_line (2, "--");
1268                  _ctrls.strip(id).set_text_line (3, "");
1269                  ++id;
1270         }
1271         if (have_well_known_eq) {
1272                         assert (id < 8);
1273                  boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -1));
1274                  _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1275                  _ctrls.strip(id).set_select_cb (cb);
1276                  _ctrls.strip(id).select_button ().set_color (0xffff00ff);
1277                  _ctrls.strip(id).select_button ().set_active (true);
1278                  _ctrls.strip(id).select_button ().set_blinking (false);
1279                  _ctrls.strip(id).set_text_line (0, "EQ");
1280                  _ctrls.strip(id).set_text_line (1, "Built-In");
1281                  _ctrls.strip(id).set_text_line (2, "--");
1282                  _ctrls.strip(id).set_text_line (3, "");
1283                  ++id;
1284         }
1285         assert (id == 8);
1286 }
1287
1288 /* ****************************************************************************
1289  * Aux Sends and Mixbus assigns
1290  */
1291
1292 void
1293 FaderPort8::assign_sends ()
1294 {
1295         boost::shared_ptr<Stripable> s = first_selected_stripable();
1296         if (!s) {
1297                 _ctrls.set_fader_mode (ModeTrack);
1298                 return;
1299         }
1300
1301         int n_sends = 0;
1302         while (0 != s->send_level_controllable (n_sends)) {
1303                 ++n_sends;
1304         }
1305         if (n_sends == 0) {
1306                 _ctrls.set_fader_mode (ModeTrack);
1307                 return;
1308         }
1309
1310         drop_ctrl_connections ();
1311         s->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1312
1313         set_periodic_display_mode (FP8Strip::SendDisplay);
1314
1315         _plugin_off = std::min (_plugin_off, n_sends - 8);
1316         _plugin_off = std::max (0, _plugin_off);
1317
1318         uint8_t id = 0;
1319         int skip = _parameter_off;
1320         for (uint32_t i = _plugin_off; ; ++i) {
1321                 if (skip > 0) {
1322                         --skip;
1323                         continue;
1324                 }
1325                 boost::shared_ptr<AutomationControl> send = s->send_level_controllable (i);
1326                 if (!send) {
1327                         break;
1328                 }
1329
1330                 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT1 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1331                 _ctrls.strip(id).set_fader_controllable (send);
1332                 _ctrls.strip(id).set_text_line (0, s->send_name (i));
1333                 _ctrls.strip(id).set_mute_controllable (s->send_enable_controllable (i));
1334
1335                 if (++id == 8) {
1336                         break;
1337                 }
1338         }
1339         // clear remaining
1340         for (; id < 8; ++id) {
1341                 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1342         }
1343 #ifdef MIXBUS // master-assign on last solo
1344         _ctrls.strip(7).set_solo_controllable (s->master_send_enable_controllable ());
1345 #endif
1346         /* set select buttons */
1347         assigned_stripable_connections.drop_connections ();
1348         _assigned_strips.clear ();
1349         assign_stripables (true);
1350 }
1351
1352 /* ****************************************************************************
1353  * Main stripable assignment (dispatch depending on mode)
1354  */
1355
1356 void
1357 FaderPort8::assign_strips (bool reset_bank)
1358 {
1359         if (reset_bank) {
1360                 _channel_off = 0;
1361         }
1362
1363         assigned_stripable_connections.drop_connections ();
1364         _assigned_strips.clear ();
1365
1366         FaderMode fadermode = _ctrls.fader_mode ();
1367         switch (fadermode) {
1368                 case ModeTrack:
1369                 case ModePan:
1370                         assign_stripables ();
1371                         stripable_selection_changed (); // update selection, automation-state
1372                         break;
1373                 case ModePlugins:
1374                         if (_proc_params.size() > 0) {
1375                                 assign_processor_ctrls ();
1376                         } else {
1377                                 spill_plugins ();
1378                         }
1379                         break;
1380                 case ModeSend:
1381                         assign_sends ();
1382                         break;
1383         }
1384 }
1385
1386 /* ****************************************************************************
1387  * some helper functions
1388  */
1389
1390 void
1391 FaderPort8::set_periodic_display_mode (FP8Strip::DisplayMode m)
1392 {
1393         for (uint8_t id = 0; id < 8; ++id) {
1394                 _ctrls.strip(id).set_periodic_display_mode (m);
1395         }
1396 }
1397
1398 void
1399 FaderPort8::drop_ctrl_connections ()
1400 {
1401         _proc_params.clear();
1402         _plugin_insert.reset ();
1403         processor_connections.drop_connections ();
1404         _showing_well_known = 0;
1405         notify_plugin_active_changed ();
1406 }
1407
1408 /* functor for FP8Strip's select button */
1409 void
1410 FaderPort8::select_strip (boost::weak_ptr<Stripable> ws)
1411 {
1412         boost::shared_ptr<Stripable> s = ws.lock();
1413         if (!s) {
1414                 return;
1415         }
1416 #if 1 /* single exclusive selection by default, toggle via shift */
1417         if (shift_mod ()) {
1418                 ToggleStripableSelection (s);
1419         } else {
1420                 SetStripableSelection (s);
1421         }
1422 #else
1423         /* tri-state selection: This allows to set the "first selected"
1424          * with a single click without clearing the selection.
1425          * Single de/select via shift.
1426          */
1427         if (shift_mod ()) {
1428                 if (s->is_selected ()) {
1429                         RemoveStripableFromSelection (s);
1430                 } else {
1431                         SetStripableSelection (s);
1432                 }
1433                 return;
1434         }
1435         if (s->is_selected () && s != first_selected_stripable ()) {
1436                 set_first_selected_stripable (s);
1437                 stripable_selection_changed ();
1438         } else {
1439                 ToggleStripableSelection (s);
1440         }
1441 #endif
1442 }
1443
1444 /* ****************************************************************************
1445  * Assigned Stripable Callbacks
1446  */
1447
1448 void
1449 FaderPort8::notify_fader_mode_changed ()
1450 {
1451         FaderMode fadermode = _ctrls.fader_mode ();
1452
1453         boost::shared_ptr<Stripable> s = first_selected_stripable();
1454         if (!s && (fadermode == ModePlugins || fadermode == ModeSend)) {
1455                 _ctrls.set_fader_mode (ModeTrack);
1456                 return;
1457         }
1458
1459         drop_ctrl_connections ();
1460
1461         switch (fadermode) {
1462                 case ModeTrack:
1463                 case ModePan:
1464                         break;
1465                 case ModePlugins:
1466                 case ModeSend:
1467                         _plugin_off = 0;
1468                         _parameter_off = 0;
1469                         // force unset rec-arm button, see also FaderPort8::button_arm
1470                         _ctrls.button (FP8Controls::BtnArm).set_active (false);
1471                         ARMButtonChange (false);
1472                         break;
1473         }
1474         assign_strips (false);
1475         notify_automation_mode_changed ();
1476 }
1477
1478 void
1479 FaderPort8::notify_stripable_added_or_removed ()
1480 {
1481         /* called by
1482          *  - DropReferences
1483          *  - session->RouteAdded
1484          *  - PresentationInfo::Change
1485          *    - Properties::hidden
1486          *    - Properties::order
1487          */
1488         assign_strips (false);
1489 }
1490
1491 /* called from static PresentationInfo::Change */
1492 void
1493 FaderPort8::notify_pi_property_changed (const PropertyChange& what_changed)
1494 {
1495         if (what_changed.contains (Properties::hidden)) {
1496                 notify_stripable_added_or_removed ();
1497         }
1498         if (what_changed.contains (Properties::order)) {
1499                 notify_stripable_added_or_removed ();
1500         }
1501         // Properties::selected is handled via StripableSelectionChanged
1502 }
1503
1504 void
1505 FaderPort8::notify_stripable_property_changed (boost::weak_ptr<Stripable> ws, const PropertyChange& what_changed)
1506 {
1507         boost::shared_ptr<Stripable> s = ws.lock();
1508         if (!s) {
1509                 assert (0); // this should not happen
1510                 return;
1511         }
1512         if (_assigned_strips.find (s) == _assigned_strips.end()) {
1513                 /* it can happen that signal emission is delayed.
1514                  * A signal may already be in the queue but the
1515                  * _assigned_strips has meanwhile changed.
1516                  *
1517                  * before _assigned_strips changes, the connections are dropped
1518                  * but that does not seem to invalidate pending requests :(
1519                  *
1520                  * Seen when creating a new MB session and Mixbusses are added
1521                  * incrementally.
1522                  */
1523                 return;
1524         }
1525         uint8_t id = _assigned_strips[s];
1526
1527         if (what_changed.contains (Properties::color)) {
1528                 _ctrls.strip(id).select_button ().set_color (s->presentation_info ().color());
1529         }
1530
1531         if (what_changed.contains (Properties::name)) {
1532                 switch (_ctrls.fader_mode ()) {
1533                         case ModeSend:
1534                                 _ctrls.strip(id).set_text_line (3, s->name(), true);
1535                                 break;
1536                         case ModeTrack:
1537                         case ModePan:
1538                                 _ctrls.strip(id).set_text_line (0, s->name());
1539                                 break;
1540                         case ModePlugins:
1541                                 assert (0);
1542                                 break;
1543                 }
1544         }
1545 }
1546
1547 void
1548 FaderPort8::stripable_selection_changed ()
1549 {
1550         if (!_device_active) {
1551                 /* this can be called anytime from the static
1552                  * ControlProtocol::StripableSelectionChanged
1553                  */
1554                 return;
1555         }
1556         automation_state_connections.drop_connections();
1557
1558         switch (_ctrls.fader_mode ()) {
1559                 case ModePlugins:
1560                         if (_proc_params.size () > 0 && _showing_well_known < 0) {
1561                                 /* w/well-known -> re-assign to new strip */
1562                                 int wk = _showing_well_known;
1563                                 drop_ctrl_connections ();
1564                                 select_plugin (wk);
1565                         }
1566                         return;
1567                 case ModeSend:
1568                         _plugin_off = 0;
1569                         assign_sends ();
1570                         return;
1571                 case ModeTrack:
1572                 case ModePan:
1573                         break;
1574         }
1575
1576         /* update selection lights */
1577         for (StripAssignmentMap::const_iterator i = _assigned_strips.begin(); i != _assigned_strips.end(); ++i) {
1578                 boost::shared_ptr<ARDOUR::Stripable> s = i->first;
1579                 uint8_t id = i->second;
1580                 bool sel = s->is_selected ();
1581                 _ctrls.strip(id).select_button ().set_active (sel);
1582                 _ctrls.strip(id).select_button ().set_blinking (sel && s == first_selected_stripable ());
1583         }
1584
1585         /* track automation-mode of primary selection */
1586         boost::shared_ptr<Stripable> s = first_selected_stripable();
1587         if (s) {
1588                 boost::shared_ptr<AutomationControl> ac;
1589                 ac = s->gain_control();
1590                 if (ac && ac->alist()) {
1591                         ac->alist()->automation_state_changed.connect (automation_state_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_automation_mode_changed, this), this);
1592                 }
1593                 ac = s->pan_azimuth_control();
1594                 if (ac && ac->alist()) {
1595                         ac->alist()->automation_state_changed.connect (automation_state_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_automation_mode_changed, this), this);
1596                 }
1597         }
1598         /* set lights */
1599         notify_automation_mode_changed ();
1600 }
1601
1602
1603 /* ****************************************************************************
1604  * Banking
1605  */
1606
1607 void
1608 FaderPort8::move_selected_into_view ()
1609 {
1610         boost::shared_ptr<Stripable> selected = first_selected_stripable ();
1611         if (!selected) {
1612                 return;
1613         }
1614
1615         StripableList strips;
1616         filter_stripables (strips);
1617
1618         StripableList::iterator it = std::find (strips.begin(), strips.end(), selected);
1619         if (it == strips.end()) {
1620                 return;
1621         }
1622         int off = std::distance (strips.begin(), it);
1623
1624         if (_channel_off <= off && off < _channel_off + 8) {
1625                 return;
1626         }
1627
1628         if (_channel_off > off) {
1629                 _channel_off = off;
1630         } else {
1631                 _channel_off = off - 7;
1632         }
1633         assign_strips (false);
1634 }
1635
1636 void
1637 FaderPort8::select_prev_next (bool next)
1638 {
1639         StripableList strips;
1640         filter_stripables (strips);
1641
1642         boost::shared_ptr<Stripable> selected = first_selected_stripable ();
1643         if (!selected) {
1644                 if (strips.size() > 0) {
1645                         if (next) {
1646                                 SetStripableSelection (strips.front ());
1647                         } else {
1648                                 SetStripableSelection (strips.back ());
1649                         }
1650                 }
1651                 return;
1652         }
1653
1654         bool found = false;
1655         boost::shared_ptr<Stripable> toselect;
1656         for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1657                 if (*s == selected) {
1658                         if (!next) {
1659                                 found = true;
1660                                 break;
1661                         }
1662                         ++s;
1663                         if (s != strips.end()) {
1664                                 toselect = *s;
1665                                 found = true;
1666                         }
1667                         break;
1668                 }
1669                 if (!next) {
1670                         toselect = *s;
1671                 }
1672         }
1673
1674         if (found && toselect) {
1675                 SetStripableSelection (toselect);
1676         }
1677 }
1678
1679 void
1680 FaderPort8::bank (bool down, bool page)
1681 {
1682         int dt = page ? 8 : 1;
1683         if (down) {
1684                 dt *= -1;
1685         }
1686         _channel_off += dt;
1687         assign_strips (false);
1688 }
1689
1690 void
1691 FaderPort8::bank_param (bool down, bool page)
1692 {
1693         int dt = page ? 8 : 1;
1694         if (down) {
1695                 dt *= -1;
1696         }
1697         switch (_ctrls.fader_mode ()) {
1698                 case ModePlugins:
1699                         if (_proc_params.size() > 0) {
1700                                 _parameter_off += dt;
1701                                 assign_processor_ctrls ();
1702                         } else {
1703                                 _plugin_off += dt;
1704                                 spill_plugins ();
1705                         }
1706                         break;
1707                 case ModeSend:
1708                         _plugin_off += dt;
1709                         assign_sends ();
1710                         break;
1711                 default:
1712                         break;
1713         }
1714 }