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