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