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