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