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