2 * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
3 * Copyright (C) 2015 Paul Davis
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.
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.
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.
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"
32 #include "midi++/port.h"
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"
50 #include "faderport8.h"
52 using namespace ARDOUR;
53 using namespace ArdourSurface;
57 using namespace ArdourSurface::FP8Types;
61 #include "pbd/abstract_ui.cc" // instantiate template
64 //#define VERBOSE_DEBUG
68 debug_2byte_msg (std::string const& msg, int b0, int b1)
71 if (DEBUG_ENABLED(DEBUG::FaderPort8)) {
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());
88 FaderPort8::FaderPort8 (Session& s)
89 : ControlProtocol (s, _("PreSonus FaderPort8"))
90 , AbstractUI<FaderPort8Request> (name())
91 , _connection_state (ConnectionState (0))
92 , _device_active (false)
96 , _show_presets (false)
97 , _showing_well_known (0)
98 , _blink_onoff (false)
102 , _link_enabled (false)
103 , _link_locked (false)
106 , _two_line_text (false)
107 , _auto_pluginui (true)
109 boost::shared_ptr<ARDOUR::Port> inp;
110 boost::shared_ptr<ARDOUR::Port> outp;
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);
117 if (_input_port == 0 || _output_port == 0) {
118 throw failed_constructor();
121 _input_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Receive)"), true));
122 _output_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Send) "), false));
124 _input_bundle->add_channel (
126 ARDOUR::DataType::MIDI,
127 session->engine().make_port_name_non_relative (inp->name())
130 _output_bundle->add_channel (
132 ARDOUR::DataType::MIDI,
133 session->engine().make_port_name_non_relative (outp->name())
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);
140 /* bind button events to call libardour actions */
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));
147 FaderPort8::~FaderPort8 ()
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
153 * stop event loop early and join thread */
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 ();
163 disconnected (); // zero faders, turn lights off, clear strips
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 ();
176 /* ****************************************************************************
181 FaderPort8::request_factory (uint32_t num_requests)
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
188 return request_buffer_factory (num_requests);
192 FaderPort8::do_request (FaderPort8Request* req)
194 if (req->type == CallSlot) {
195 call_slot (MISSING_INVALIDATOR, req->the_slot);
196 } else if (req->type == Quit) {
205 DEBUG_TRACE (DEBUG::FaderPort8, "BaseUI::quit ()\n");
207 close (); // drop references, disconnect from session signals
211 FaderPort8::thread_init ()
213 struct sched_param rtparam;
215 pthread_set_name (event_loop_name().c_str());
217 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
218 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
220 memset (&rtparam, 0, sizeof (rtparam));
221 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
223 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
224 // do we care? not particularly.
229 FaderPort8::periodic ()
231 /* prepare TC display -- handled by stripable Periodic ()
232 * in FP8Strip::periodic_update_timecode
234 if (_ctrls.display_timecode () && clock_mode ()) {
236 session->timecode_time (TC);
237 _timecode = Timecode::timecode_format_time(TC);
240 Timecode::BBT_Time BBT = session->tempo_map ().bbt_at_frame (session->transport_frame ());
241 snprintf (buf, sizeof (buf),
242 " %02" PRIu32 "|%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32,
243 BBT.bars % 100, BBT.beats %100,
244 (BBT.ticks/ 100) %100, BBT.ticks %100);
245 _musical_time = std::string (buf);
248 _musical_time.clear ();
251 /* update stripables */
257 FaderPort8::blink_it ()
259 _blink_onoff = !_blink_onoff;
260 BlinkIt (_blink_onoff);
264 /* ****************************************************************************
265 * Port and Signal Connection Management
268 FaderPort8::set_active (bool yn)
270 DEBUG_TRACE (DEBUG::FaderPort8, string_compose("set_active init with yn: '%1'\n", yn));
272 if (yn == active()) {
277 /* start event loop */
279 connect_session_signals ();
284 ControlProtocol::set_active (yn);
285 DEBUG_TRACE (DEBUG::FaderPort8, string_compose("set_active done with yn: '%1'\n", yn));
292 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::close\n");
293 stop_midi_handling ();
294 session_connections.drop_connections ();
295 automation_state_connections.drop_connections ();
296 assigned_stripable_connections.drop_connections ();
297 _assigned_strips.clear ();
298 drop_ctrl_connections ();
299 port_connections.drop_connections ();
300 selection_connection.disconnect ();
304 FaderPort8::stop_midi_handling ()
306 _periodic_connection.disconnect ();
307 _blink_connection.disconnect ();
308 midi_connections.drop_connections ();
309 /* Note: the input handler is still active at this point, but we're no
310 * longer connected to any of the parser signals
315 FaderPort8::connected ()
317 DEBUG_TRACE (DEBUG::FaderPort8, "initializing\n");
318 assert (!_device_active);
320 if (_device_active) {
321 stop_midi_handling (); // re-init
324 // ideally check firmware version >= 1.01 (USB bcdDevice 0x0101) (vendor 0x194f prod 0x0202)
325 // but we don't have a handle to the underlying USB device here.
327 memset (_channel_off, 0, sizeof (_channel_off));
328 _plugin_off = _parameter_off = 0;
329 _blink_onoff = false;
333 start_midi_handling ();
334 _ctrls.initialize ();
336 /* highlight bound user-actions */
337 for (FP8Controls::UserButtonMap::const_iterator i = _ctrls.user_buttons ().begin ();
338 i != _ctrls.user_buttons ().end (); ++i) {
339 _ctrls.button (i->first).set_active (! _user_action_map[i->first].empty ());
341 /* shift button lights */
342 tx_midi3 (0x90, 0x06, 0x00);
343 tx_midi3 (0x90, 0x46, 0x00);
345 send_session_state ();
348 Glib::RefPtr<Glib::TimeoutSource> blink_timer =
349 Glib::TimeoutSource::create (200);
350 _blink_connection = blink_timer->connect (sigc::mem_fun (*this, &FaderPort8::blink_it));
351 blink_timer->attach (main_loop()->get_context());
353 Glib::RefPtr<Glib::TimeoutSource> periodic_timer =
354 Glib::TimeoutSource::create (100);
355 _periodic_connection = periodic_timer->connect (sigc::mem_fun (*this, &FaderPort8::periodic));
356 periodic_timer->attach (main_loop()->get_context());
360 FaderPort8::disconnected ()
362 stop_midi_handling ();
363 if (_device_active) {
364 for (uint8_t id = 0; id < 8; ++id) {
365 _ctrls.strip(id).unset_controllables ();
367 _ctrls.all_lights_off ();
372 FaderPort8::engine_reset ()
374 /* Port::PortDrop is called when the engine is halted or stopped */
375 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::engine_reset\n");
376 _connection_state = 0;
377 _device_active = false;
382 FaderPort8::connection_handler (std::string name1, std::string name2)
385 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::connection_handler: start\n");
387 if (!_input_port || !_output_port) {
391 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
392 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
394 if (ni == name1 || ni == name2) {
395 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connection notify %1 and %2\n", name1, name2));
396 if (_input_port->connected ()) {
397 if (_connection_state & InputConnected) {
400 _connection_state |= InputConnected;
402 _connection_state &= ~InputConnected;
404 } else if (no == name1 || no == name2) {
405 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connection notify %1 and %2\n", name1, name2));
406 if (_output_port->connected ()) {
407 if (_connection_state & OutputConnected) {
410 _connection_state |= OutputConnected;
412 _connection_state &= ~OutputConnected;
416 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
422 if ((_connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
424 /* XXX this is a horrible hack. Without a short sleep here,
425 * something prevents the device wakeup messages from being
426 * sent and/or the responses from being received.
429 DEBUG_TRACE (DEBUG::FaderPort8, "device now connected for both input and output\n");
431 _device_active = true;
434 DEBUG_TRACE (DEBUG::FaderPort8, "Device disconnected (input or output or both) or not yet fully connected\n");
435 if (_device_active) {
438 _device_active = false;
441 ConnectionChange (); /* emit signal for our GUI */
444 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::connection_handler: end\n");
447 return true; /* connection status changed */
450 list<boost::shared_ptr<ARDOUR::Bundle> >
451 FaderPort8::bundles ()
453 list<boost::shared_ptr<ARDOUR::Bundle> > b;
456 b.push_back (_input_bundle);
457 b.push_back (_output_bundle);
463 /* ****************************************************************************
467 FaderPort8::midi_input_handler (Glib::IOCondition ioc, boost::weak_ptr<ARDOUR::AsyncMIDIPort> wport)
469 boost::shared_ptr<AsyncMIDIPort> port (wport.lock());
471 if (!port || !_input_port) {
476 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("something happend on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
487 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("data available on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
489 framepos_t now = session->engine().sample_time();
497 FaderPort8::start_midi_handling ()
499 _input_port->parser()->sysex.connect_same_thread (midi_connections, boost::bind (&FaderPort8::sysex_handler, this, _1, _2, _3));
500 _input_port->parser()->poly_pressure.connect_same_thread (midi_connections, boost::bind (&FaderPort8::polypressure_handler, this, _1, _2));
501 for (uint8_t i = 0; i < 16; ++i) {
502 _input_port->parser()->channel_pitchbend[i].connect_same_thread (midi_connections, boost::bind (&FaderPort8::pitchbend_handler, this, _1, i, _2));
504 _input_port->parser()->controller.connect_same_thread (midi_connections, boost::bind (&FaderPort8::controller_handler, this, _1, _2));
505 _input_port->parser()->note_on.connect_same_thread (midi_connections, boost::bind (&FaderPort8::note_on_handler, this, _1, _2));
506 _input_port->parser()->note_off.connect_same_thread (midi_connections, boost::bind (&FaderPort8::note_off_handler, this, _1, _2));
508 /* This connection means that whenever data is ready from the input
509 * port, the relevant thread will invoke our ::midi_input_handler()
510 * method, which will read the data, and invoke the parser.
512 _input_port->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &FaderPort8::midi_input_handler), boost::weak_ptr<AsyncMIDIPort> (_input_port)));
513 _input_port->xthread().attach (main_loop()->get_context());
517 FaderPort8::tx_midi (std::vector<uint8_t> const& d) const
519 /* work around midi buffer overflow for batch changes */
520 if (d.size() == 3 && (d[0] == 0x91 || d[0] == 0x92)) {
521 /* set colors triplet in one go */
522 } else if (d.size() == 3 && (d[0] == 0x93)) {
525 g_usleep (400 * d.size());
528 size_t tx = _output_port->write (&d[0], d.size(), 0);
529 assert (tx == d.size());
532 return _output_port->write (&d[0], d.size(), 0);
536 /* ****************************************************************************
540 FaderPort8::polypressure_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
542 debug_2byte_msg ("PP", tb->controller_number, tb->value);
543 // outgoing only (meter)
547 FaderPort8::pitchbend_handler (MIDI::Parser &, uint8_t chan, MIDI::pitchbend_t pb)
549 debug_2byte_msg ("PB", chan, pb);
550 /* fader 0..16368 (0x3ff0 -- 1024 steps) */
551 bool handled = _ctrls.midi_fader (chan, pb);
552 /* if Shift key is held while moving a fader (group override), don't lock shift. */
553 if ((_shift_pressed > 0) && handled) {
554 _shift_connection.disconnect ();
560 FaderPort8::controller_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
562 debug_2byte_msg ("CC", tb->controller_number, tb->value);
564 // val Bit 7 = direction, Bits 0-6 = number of steps
565 if (tb->controller_number == 0x3c) {
566 encoder_navigate (tb->value & 0x40 ? true : false, tb->value & 0x3f);
568 if (tb->controller_number == 0x10) {
569 encoder_parameter (tb->value & 0x40 ? true : false, tb->value & 0x3f);
574 FaderPort8::note_on_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
576 debug_2byte_msg ("ON", tb->note_number, tb->velocity);
579 if (tb->note_number >= 0x68 && tb->note_number <= 0x6f) {
580 _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
584 /* special case shift */
585 if (tb->note_number == 0x06 || tb->note_number == 0x46) {
586 _shift_pressed |= (tb->note_number == 0x06) ? 1 : 2;
587 if (_shift_pressed == 3) {
590 _shift_connection.disconnect ();
593 ShiftButtonChange (false);
594 tx_midi3 (0x90, 0x06, 0x00);
595 tx_midi3 (0x90, 0x46, 0x00);
599 Glib::RefPtr<Glib::TimeoutSource> shift_timer =
600 Glib::TimeoutSource::create (1000);
601 shift_timer->attach (main_loop()->get_context());
602 _shift_connection = shift_timer->connect (sigc::mem_fun (*this, &FaderPort8::shift_timeout));
604 ShiftButtonChange (true);
605 tx_midi3 (0x90, 0x06, 0x7f);
606 tx_midi3 (0x90, 0x46, 0x7f);
610 _ctrls.midi_event (tb->note_number, tb->velocity);
614 FaderPort8::note_off_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
616 debug_2byte_msg ("OF", tb->note_number, tb->velocity);
618 if (tb->note_number >= 0x68 && tb->note_number <= 0x6f) {
620 _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
624 /* special case shift */
625 if (tb->note_number == 0x06 || tb->note_number == 0x46) {
626 _shift_pressed &= (tb->note_number == 0x06) ? 2 : 1;
627 if (_shift_pressed > 0) {
633 ShiftButtonChange (false);
634 tx_midi3 (0x90, 0x06, 0x00);
635 tx_midi3 (0x90, 0x46, 0x00);
636 /* just in case this happens concurrently */
637 _shift_connection.disconnect ();
642 bool handled = _ctrls.midi_event (tb->note_number, tb->velocity);
643 /* if Shift key is held while activating an action, don't lock shift. */
644 if ((_shift_pressed > 0) && handled) {
645 _shift_connection.disconnect ();
651 FaderPort8::sysex_handler (MIDI::Parser &p, MIDI::byte *buf, size_t size)
654 if (DEBUG_ENABLED(DEBUG::FaderPort8)) {
656 DEBUG_STR_APPEND(a, string_compose ("RECV sysex siz=%1", size));
657 for (size_t i=0; i < size; ++i) {
658 DEBUG_STR_APPEND(a,hex);
659 DEBUG_STR_APPEND(a,"0x");
660 DEBUG_STR_APPEND(a,(int)buf[i]);
661 DEBUG_STR_APPEND(a,' ');
663 DEBUG_STR_APPEND(a,'\n');
664 DEBUG_TRACE (DEBUG::FaderPort8, DEBUG_STR(a).str());
669 /* ****************************************************************************
673 FaderPort8::set_button_action (FP8Controls::ButtonId id, bool press, std::string const& action_name)
675 if (_ctrls.user_buttons().find (id) == _ctrls.user_buttons().end ()) {
678 _user_action_map[id].action (press).assign_action (action_name);
680 if (!_device_active) {
683 _ctrls.button (id).set_active (!_user_action_map[id].empty ());
687 FaderPort8::get_button_action (FP8Controls::ButtonId id, bool press)
689 return _user_action_map[id].action(press)._action_name;
692 /* ****************************************************************************
696 FaderPort8::get_state ()
698 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::get_state\n");
699 XMLNode& node (ControlProtocol::get_state());
703 child = new XMLNode (X_("Input"));
704 child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_input_port)->get_state());
705 node.add_child_nocopy (*child);
707 child = new XMLNode (X_("Output"));
708 child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_output_port)->get_state());
709 node.add_child_nocopy (*child);
711 node.set_property (X_("clock-mode"), _clock_mode);
712 node.set_property (X_("scribble-mode"), _scribble_mode);
713 node.set_property (X_("two-line-text"), _two_line_text);
715 for (UserActionMap::const_iterator i = _user_action_map.begin (); i != _user_action_map.end (); ++i) {
716 if (i->second.empty()) {
720 if (!_ctrls.button_enum_to_name (i->first, name)) {
723 XMLNode* btn = new XMLNode (X_("Button"));
724 btn->set_property (X_("id"), name);
725 if (!i->second.action(true).empty ()) {
726 btn->set_property ("press", i->second.action(true)._action_name);
728 if (!i->second.action(false).empty ()) {
729 btn->set_property ("release", i->second.action(false)._action_name);
731 node.add_child_nocopy (*btn);
738 FaderPort8::set_state (const XMLNode& node, int version)
740 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state\n");
742 XMLNodeConstIterator niter;
743 XMLNode const* child;
745 if (ControlProtocol::set_state (node, version)) {
749 if ((child = node.child (X_("Input"))) != 0) {
750 XMLNode* portnode = child->child (Port::state_node_name.c_str());
752 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state Input\n");
753 boost::shared_ptr<ARDOUR::Port>(_input_port)->set_state (*portnode, version);
757 if ((child = node.child (X_("Output"))) != 0) {
758 XMLNode* portnode = child->child (Port::state_node_name.c_str());
760 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state Output\n");
761 boost::shared_ptr<ARDOUR::Port>(_output_port)->set_state (*portnode, version);
765 node.get_property (X_("clock-mode"), _clock_mode);
766 node.get_property (X_("scribble-mode"), _scribble_mode);
767 node.get_property (X_("two-line-text"), _two_line_text);
769 _user_action_map.clear ();
770 // TODO: When re-loading state w/o surface re-init becomes possible,
771 // unset lights and reset colors of user buttons.
773 for (XMLNodeList::const_iterator n = node.children().begin(); n != node.children().end(); ++n) {
774 if ((*n)->name() != X_("Button")) {
779 if (!(*n)->get_property (X_("id"), id_str)) {
783 FP8Controls::ButtonId id;
784 if (!_ctrls.button_name_to_enum (id_str, id)) {
788 std::string action_str;
789 if ((*n)->get_property (X_("press"), action_str)) {
790 set_button_action (id, true, action_str);
792 if ((*n)->get_property (X_("release"), action_str)) {
793 set_button_action (id, false, action_str);
800 /* ****************************************************************************
801 * Stripable Assignment
804 static bool flt_audio_track (boost::shared_ptr<Stripable> s) {
805 return boost::dynamic_pointer_cast<AudioTrack>(s) != 0;
808 static bool flt_midi_track (boost::shared_ptr<Stripable> s) {
809 return boost::dynamic_pointer_cast<MidiTrack>(s) != 0;
812 static bool flt_bus (boost::shared_ptr<Stripable> s) {
813 if (boost::dynamic_pointer_cast<Route>(s) == 0) {
817 if (s->mixbus () == 0) {
821 return boost::dynamic_pointer_cast<Track>(s) == 0;
824 static bool flt_auxbus (boost::shared_ptr<Stripable> s) {
825 if (boost::dynamic_pointer_cast<Route>(s) == 0) {
829 if (s->mixbus () > 0) {
833 return boost::dynamic_pointer_cast<Track>(s) == 0;
836 static bool flt_vca (boost::shared_ptr<Stripable> s) {
837 return boost::dynamic_pointer_cast<VCA>(s) != 0;
840 static bool flt_selected (boost::shared_ptr<Stripable> s) {
841 return s->is_selected ();
844 static bool flt_mains (boost::shared_ptr<Stripable> s) {
845 return (s->is_master() || s->is_monitor());
848 static bool flt_all (boost::shared_ptr<Stripable> s) {
852 static bool flt_rec_armed (boost::shared_ptr<Stripable> s) {
853 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
857 return t->rec_enable_control ()->get_value () > 0.;
860 static bool flt_instrument (boost::shared_ptr<Stripable> s) {
861 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(s);
865 return 0 != r->the_instrument ();
869 FaderPort8::filter_stripables (StripableList& strips) const
871 typedef bool (*FilterFunction)(boost::shared_ptr<Stripable>);
874 bool allow_master = false;
875 bool allow_monitor = false;
877 switch (_ctrls.mix_mode ()) {
879 flt = &flt_audio_track;
882 flt = &flt_instrument;
891 flt = &flt_midi_track;
899 allow_monitor = true;
903 flt = &flt_rec_armed;
918 session->get_stripables (all);
920 for (StripableList::const_iterator s = all.begin(); s != all.end(); ++s) {
921 if ((*s)->is_auditioner ()) { continue; }
922 if ((*s)->is_hidden ()) { continue; }
924 if (!allow_master && (*s)->is_master ()) { continue; }
925 if (!allow_monitor && (*s)->is_monitor ()) { continue; }
928 strips.push_back (*s);
931 strips.sort (Stripable::Sorter(true));
934 /* Track/Pan mode: assign stripable to strips, Send-mode: selection */
936 FaderPort8::assign_stripables (bool select_only)
938 StripableList strips;
939 filter_stripables (strips);
942 set_periodic_display_mode (FP8Strip::Stripables);
945 int n_strips = strips.size();
946 int channel_off = get_channel_off (_ctrls.mix_mode ());
947 channel_off = std::min (channel_off, n_strips - 8);
948 channel_off = std::max (0, channel_off);
949 set_channel_off (_ctrls.mix_mode (), channel_off);
952 int skip = channel_off;
953 for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
959 _assigned_strips[*s] = id;
960 (*s)->DropReferences.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
961 boost::bind (&FaderPort8::notify_stripable_added_or_removed, this), this);
963 (*s)->PropertyChanged.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
964 boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
965 (*s)->presentation_info ().PropertyChanged.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
966 boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
969 /* used in send mode */
970 _ctrls.strip(id).set_text_line (3, (*s)->name (), true);
971 _ctrls.strip(id).select_button ().set_color ((*s)->presentation_info ().color());
972 /* update selection lights */
973 _ctrls.strip(id).select_button ().set_active ((*s)->is_selected ());
974 _ctrls.strip(id).select_button ().set_blinking (*s == first_selected_stripable ());
976 _ctrls.strip(id).set_stripable (*s, _ctrls.fader_mode() == ModePan);
979 boost::function<void ()> cb (boost::bind (&FaderPort8::select_strip, this, boost::weak_ptr<Stripable> (*s)));
980 _ctrls.strip(id).set_select_cb (cb);
986 for (; id < 8; ++id) {
987 _ctrls.strip(id).unset_controllables (select_only ? (FP8Strip::CTRL_SELECT | FP8Strip::CTRL_TEXT3) : FP8Strip::CTRL_ALL);
988 _ctrls.strip(id).set_periodic_display_mode (FP8Strip::Stripables);
992 /* ****************************************************************************
997 FaderPort8::unlock_link (bool drop)
999 link_locked_connection.disconnect ();
1002 stop_link (); // calls back here with drop = false
1006 _link_locked = false;
1008 if (_link_enabled) {
1009 assert (_ctrls.button (FP8Controls::BtnLink).is_active ());
1010 _link_control.reset ();
1011 start_link (); // re-connect & update LED colors
1013 _ctrls.button (FP8Controls::BtnLink).set_active (false);
1014 _ctrls.button (FP8Controls::BtnLink).set_color (0x888888ff);
1015 _ctrls.button (FP8Controls::BtnLock).set_active (false);
1016 _ctrls.button (FP8Controls::BtnLock).set_color (0x888888ff);
1021 FaderPort8::lock_link ()
1023 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (_link_control.lock ());
1027 ac->DropReferences.connect (link_locked_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::unlock_link, this, true), this);
1029 // stop watching for focus events
1030 link_connection.disconnect ();
1032 _link_locked = true;
1034 _ctrls.button (FP8Controls::BtnLock).set_color (0x00ff00ff);
1035 _ctrls.button (FP8Controls::BtnLink).set_color (0x00ff00ff);
1039 FaderPort8::stop_link ()
1041 if (!_link_enabled) {
1044 link_connection.disconnect ();
1045 _link_control.reset ();
1046 _link_enabled = false;
1047 unlock_link (); // also updates button colors
1051 FaderPort8::start_link ()
1053 assert (!_link_locked);
1055 _link_enabled = true;
1056 _ctrls.button (FP8Controls::BtnLink).set_active (true);
1057 _ctrls.button (FP8Controls::BtnLock).set_active (true);
1058 nofity_focus_control (_link_control); // update BtnLink, BtnLock colors
1060 PBD::Controllable::GUIFocusChanged.connect (link_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::nofity_focus_control, this, _1), this);
1064 /* ****************************************************************************
1065 * Plugin selection and parameters
1069 FaderPort8::toggle_preset_param_mode ()
1071 FaderMode fadermode = _ctrls.fader_mode ();
1072 if (fadermode != ModePlugins || _proc_params.size() == 0) {
1075 _show_presets = ! _show_presets;
1076 assign_processor_ctrls ();
1080 FaderPort8::preset_changed ()
1082 if (_show_presets) {
1083 assign_processor_ctrls ();
1088 FaderPort8::assign_processor_ctrls ()
1090 if (_proc_params.size() == 0) {
1091 _ctrls.set_fader_mode (ModeTrack);
1094 set_periodic_display_mode (FP8Strip::PluginParam);
1096 if (_show_presets) {
1097 if (assign_plugin_presets (_plugin_insert.lock ())) {
1100 _show_presets = false;
1103 std::vector <ProcessorCtrl*> toggle_params;
1104 std::vector <ProcessorCtrl*> slider_params;
1106 for ( std::list <ProcessorCtrl>::iterator i = _proc_params.begin(); i != _proc_params.end(); ++i) {
1107 if ((*i).ac->toggled()) {
1108 toggle_params.push_back (&(*i));
1110 slider_params.push_back (&(*i));
1114 int n_parameters = std::max (toggle_params.size(), slider_params.size());
1116 _parameter_off = std::min (_parameter_off, n_parameters - 8);
1117 _parameter_off = std::max (0, _parameter_off);
1120 for (size_t i = _parameter_off; i < (size_t)n_parameters; ++i) {
1121 if (i >= toggle_params.size ()) {
1122 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT01 & ~FP8Strip::CTRL_TEXT2);
1124 else if (i >= slider_params.size ()) {
1125 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_SELECT & ~FP8Strip::CTRL_TEXT3);
1127 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1130 if (i < slider_params.size ()) {
1131 _ctrls.strip(id).set_fader_controllable (slider_params[i]->ac);
1132 std::string param_name = slider_params[i]->name;
1133 _ctrls.strip(id).set_text_line (0, param_name.substr (0, 9));
1134 _ctrls.strip(id).set_text_line (1, param_name.length () > 9 ? param_name.substr (9) : "");
1136 if (i < toggle_params.size ()) {
1137 _ctrls.strip(id).set_select_controllable (toggle_params[i]->ac);
1138 _ctrls.strip(id).set_text_line (3, toggle_params[i]->name, true);
1146 for (; id < 8; ++id) {
1147 _ctrls.strip(id).unset_controllables ();
1152 FaderPort8::assign_plugin_presets (boost::shared_ptr<PluginInsert> pi)
1157 boost::shared_ptr<ARDOUR::Plugin> plugin = pi->plugin ();
1159 std::vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets ();
1160 if (presets.size () == 0) {
1164 int n_parameters = presets.size ();
1166 _parameter_off = std::min (_parameter_off, n_parameters - 7);
1167 _parameter_off = std::max (0, _parameter_off);
1168 Plugin::PresetRecord active = plugin->last_preset ();
1171 for (size_t i = _parameter_off; i < (size_t)n_parameters; ++i) {
1172 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT01 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1173 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin_preset, this, i));
1174 _ctrls.strip(id).set_select_cb (cb);
1175 _ctrls.strip(id).select_button ().set_active (true);
1176 if (active != presets.at(i)) {
1177 _ctrls.strip(id).select_button ().set_color (0x0000ffff);
1178 _ctrls.strip(id).select_button ().set_blinking (false);
1180 _ctrls.strip(id).select_button ().set_color (0x00ffffff);
1181 _ctrls.strip(id).select_button ().set_blinking (plugin->parameter_changed_since_last_preset ());
1183 std::string label = presets.at(i).label;
1184 _ctrls.strip(id).set_text_line (0, label.substr (0, 9));
1185 _ctrls.strip(id).set_text_line (1, label.length () > 9 ? label.substr (9) : "");
1186 _ctrls.strip(id).set_text_line (3, "PRESET", true);
1193 for (; id < 7; ++id) {
1194 _ctrls.strip(id).unset_controllables ();
1197 // pin clear-preset to the last slot
1199 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1200 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin_preset, this, SIZE_MAX));
1201 _ctrls.strip(id).set_select_cb (cb);
1202 _ctrls.strip(id).select_button ().set_blinking (false);
1203 _ctrls.strip(id).select_button ().set_color (active.uri.empty() ? 0x00ffffff : 0x0000ffff);
1204 _ctrls.strip(id).select_button ().set_active (true);
1205 _ctrls.strip(id).set_text_line (0, _("(none)"));
1206 _ctrls.strip(id).set_text_line (3, "PRESET", true);
1211 FaderPort8::build_well_known_processor_ctrls (boost::shared_ptr<Stripable> s, bool eq)
1213 #define PUSH_BACK_NON_NULL(N, C) do {if (C) { _proc_params.push_back (ProcessorCtrl (N, C)); }} while (0)
1215 _proc_params.clear ();
1217 int cnt = s->eq_band_cnt();
1220 PUSH_BACK_NON_NULL ("Flt In", s->filter_enable_controllable (true)); // both HP/LP
1221 PUSH_BACK_NON_NULL ("HP Freq", s->filter_freq_controllable (true));
1222 PUSH_BACK_NON_NULL ("LP Freq", s->filter_freq_controllable (false));
1223 PUSH_BACK_NON_NULL ("EQ In", s->eq_enable_controllable ());
1224 #elif defined (MIXBUS)
1225 PUSH_BACK_NON_NULL ("EQ In", s->eq_enable_controllable ());
1226 PUSH_BACK_NON_NULL ("HP Freq", s->filter_freq_controllable (true));
1229 for (int band = 0; band < cnt; ++band) {
1230 std::string bn = s->eq_band_name (band);
1231 PUSH_BACK_NON_NULL (string_compose ("Gain %1", bn), s->eq_gain_controllable (band));
1232 PUSH_BACK_NON_NULL (string_compose ("Freq %1", bn), s->eq_freq_controllable (band));
1233 PUSH_BACK_NON_NULL (string_compose ("Band %1", bn), s->eq_q_controllable (band));
1234 PUSH_BACK_NON_NULL (string_compose ("Shape %1", bn), s->eq_shape_controllable (band));
1237 PUSH_BACK_NON_NULL ("Comp In", s->comp_enable_controllable ());
1238 PUSH_BACK_NON_NULL ("Threshold", s->comp_threshold_controllable ());
1239 PUSH_BACK_NON_NULL ("Speed", s->comp_speed_controllable ());
1240 PUSH_BACK_NON_NULL ("Mode", s->comp_mode_controllable ());
1245 FaderPort8::select_plugin (int num)
1247 // make sure drop_ctrl_connections() was called
1248 assert (_proc_params.size() == 0 && _showing_well_known == 0 && _plugin_insert.expired());
1250 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (first_selected_stripable());
1252 _ctrls.set_fader_mode (ModeTrack);
1259 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (r->nth_plugin (num));
1261 if (pi && !pi->is_channelstrip () && pi->display_to_user ())
1263 if (pi && pi->display_to_user ())
1266 pi->enable (! pi->enabled ());
1273 build_well_known_processor_ctrls (r, num == -1);
1274 assign_processor_ctrls ();
1275 _showing_well_known = num;
1278 _showing_well_known = 0;
1280 boost::shared_ptr<Processor> proc = r->nth_plugin (num);
1282 _ctrls.set_fader_mode (ModeTrack);
1286 // disconnect signals from spill_plugins: processors_changed and ActiveChanged
1287 processor_connections.drop_connections ();
1288 r->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1290 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1291 assert (pi); // nth_plugin() always returns a PI.
1292 /* _plugin_insert is used for Bypass/Enable & presets */
1294 if (!pi->is_channelstrip () && pi->display_to_user ())
1296 if (pi->display_to_user ())
1299 _plugin_insert = boost::weak_ptr<ARDOUR::PluginInsert> (pi);
1300 pi->ActiveChanged.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_plugin_active_changed, this), this);
1301 boost::shared_ptr<ARDOUR::Plugin> plugin = pi->plugin ();
1303 plugin->PresetAdded.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
1304 plugin->PresetRemoved.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
1305 plugin->PresetLoaded.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
1306 plugin->PresetDirty.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
1308 if (_auto_pluginui) {
1309 pi->ShowUI (); /* EMIT SIGNAL */
1313 // switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed()
1314 // which drops the references, disconnects the signal and re-spills tracks
1315 proc->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1318 _proc_params.clear();
1319 set<Evoral::Parameter> p = proc->what_can_be_automated ();
1320 for (set<Evoral::Parameter>::iterator i = p.begin(); i != p.end(); ++i) {
1321 std::string n = proc->describe_parameter (*i);
1322 if (n == "hidden") {
1325 _proc_params.push_back (ProcessorCtrl (n, proc->automation_control (*i)));
1328 // TODO: open plugin GUI if (_proc_params.size() > 0)
1331 assign_processor_ctrls ();
1332 notify_plugin_active_changed ();
1336 FaderPort8::select_plugin_preset (size_t num)
1338 assert (_proc_params.size() > 0);
1339 boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock();
1341 _ctrls.set_fader_mode (ModeTrack);
1344 if (num == SIZE_MAX) {
1345 pi->plugin ()->clear_preset ();
1347 std::vector<ARDOUR::Plugin::PresetRecord> presets = pi->plugin ()->get_presets ();
1348 if (num < presets.size ()) {
1349 pi->load_preset (presets.at (num));
1352 _show_presets = false;
1353 assign_processor_ctrls ();
1356 /* short 4 chars at most */
1357 static std::string plugintype (ARDOUR::PluginType t) {
1374 return enum_2_string (t);
1378 FaderPort8::spill_plugins ()
1380 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (first_selected_stripable());
1382 _ctrls.set_fader_mode (ModeTrack);
1386 drop_ctrl_connections ();
1388 // switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed()
1389 // which drops the references, disconnects the signal and re-spills tracks
1390 r->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1392 // update when processor change
1393 r->processors_changed.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::spill_plugins, this), this);
1396 boost::shared_ptr<Processor> proc;
1398 std::vector<uint32_t> procs;
1400 for (uint32_t i = 0; 0 != (proc = r->nth_plugin (i)); ++i) {
1401 if (!proc->display_to_user ()) {
1403 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1404 if (pi->is_channelstrip ()) // don't skip MB PRE
1409 set<Evoral::Parameter> p = proc->what_can_be_automated ();
1410 for (set<Evoral::Parameter>::iterator j = p.begin(); j != p.end(); ++j) {
1411 std::string n = proc->describe_parameter (*j);
1412 if (n == "hidden") {
1417 if (n_controls > 0) {
1418 procs.push_back (i);
1422 int n_plugins = procs.size();
1424 bool have_well_known_eq = false;
1425 bool have_well_known_comp = false;
1427 // reserve last slot(s) for "well-known"
1428 if (r->eq_band_cnt() > 0) {
1430 have_well_known_eq = true;
1432 if (r->comp_enable_controllable ()) {
1434 have_well_known_comp = true;
1437 if (n_plugins == 0 && !have_well_known_eq && !have_well_known_comp) {
1438 _ctrls.set_fader_mode (ModeTrack);
1442 set_periodic_display_mode (FP8Strip::PluginSelect);
1444 _plugin_off = std::min (_plugin_off, n_plugins - spillwidth);
1445 _plugin_off = std::max (0, _plugin_off);
1448 for (uint32_t i = _plugin_off; ; ++i) {
1449 if (i >= procs.size()) {
1452 boost::shared_ptr<Processor> proc = r->nth_plugin (procs[i]);
1456 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1457 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, procs[i]));
1459 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1460 _ctrls.strip(id).set_select_cb (cb);
1461 _ctrls.strip(id).select_button ().set_color (proc->enabled () ? 0x00ff00ff : 0xff0000ff);
1462 _ctrls.strip(id).select_button ().set_active (true);
1463 _ctrls.strip(id).select_button ().set_blinking (false);
1464 _ctrls.strip(id).set_text_line (0, proc->name());
1465 _ctrls.strip(id).set_text_line (1, pi->plugin()->maker());
1466 _ctrls.strip(id).set_text_line (2, plugintype (pi->type()));
1467 _ctrls.strip(id).set_text_line (3, "");
1469 pi->ActiveChanged.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::spill_plugins, this), this);
1471 if (++id == spillwidth) {
1476 for (; id < spillwidth; ++id) {
1477 _ctrls.strip(id).unset_controllables ();
1480 if (have_well_known_comp) {
1482 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -2));
1483 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1484 _ctrls.strip(id).set_select_cb (cb);
1485 _ctrls.strip(id).select_button ().set_color (0xffff00ff);
1486 _ctrls.strip(id).select_button ().set_active (true);
1487 _ctrls.strip(id).select_button ().set_blinking (false);
1488 _ctrls.strip(id).set_text_line (0, "Comp");
1489 _ctrls.strip(id).set_text_line (1, "Built-In");
1490 _ctrls.strip(id).set_text_line (2, "--");
1491 _ctrls.strip(id).set_text_line (3, "");
1494 if (have_well_known_eq) {
1496 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -1));
1497 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1498 _ctrls.strip(id).set_select_cb (cb);
1499 _ctrls.strip(id).select_button ().set_color (0xffff00ff);
1500 _ctrls.strip(id).select_button ().set_active (true);
1501 _ctrls.strip(id).select_button ().set_blinking (false);
1502 _ctrls.strip(id).set_text_line (0, "EQ");
1503 _ctrls.strip(id).set_text_line (1, "Built-In");
1504 _ctrls.strip(id).set_text_line (2, "--");
1505 _ctrls.strip(id).set_text_line (3, "");
1511 /* ****************************************************************************
1512 * Aux Sends and Mixbus assigns
1516 FaderPort8::assign_sends ()
1518 boost::shared_ptr<Stripable> s = first_selected_stripable();
1520 _ctrls.set_fader_mode (ModeTrack);
1525 while (0 != s->send_level_controllable (n_sends)) {
1529 _ctrls.set_fader_mode (ModeTrack);
1533 drop_ctrl_connections ();
1534 s->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1536 set_periodic_display_mode (FP8Strip::SendDisplay);
1538 _plugin_off = std::min (_plugin_off, n_sends - 8);
1539 _plugin_off = std::max (0, _plugin_off);
1542 int skip = _parameter_off;
1543 for (uint32_t i = _plugin_off; ; ++i) {
1548 boost::shared_ptr<AutomationControl> send = s->send_level_controllable (i);
1553 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT01 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1554 _ctrls.strip(id).set_fader_controllable (send);
1555 _ctrls.strip(id).set_text_line (0, s->send_name (i));
1556 _ctrls.strip(id).set_mute_controllable (s->send_enable_controllable (i));
1563 for (; id < 8; ++id) {
1564 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1566 #ifdef MIXBUS // master-assign on last solo
1567 _ctrls.strip(7).set_solo_controllable (s->master_send_enable_controllable ());
1569 /* set select buttons */
1570 assigned_stripable_connections.drop_connections ();
1571 _assigned_strips.clear ();
1572 assign_stripables (true);
1575 /* ****************************************************************************
1576 * Main stripable assignment (dispatch depending on mode)
1580 FaderPort8::assign_strips ()
1582 assigned_stripable_connections.drop_connections ();
1583 _assigned_strips.clear ();
1585 FaderMode fadermode = _ctrls.fader_mode ();
1586 switch (fadermode) {
1589 assign_stripables ();
1590 stripable_selection_changed (); // update selection, automation-state
1593 if (_proc_params.size() > 0) {
1594 assign_processor_ctrls ();
1605 /* ****************************************************************************
1606 * some helper functions
1610 FaderPort8::set_periodic_display_mode (FP8Strip::DisplayMode m)
1612 for (uint8_t id = 0; id < 8; ++id) {
1613 _ctrls.strip(id).set_periodic_display_mode (m);
1618 FaderPort8::drop_ctrl_connections ()
1620 _proc_params.clear();
1621 if (_auto_pluginui) {
1622 boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock ();
1624 pi->HideUI (); /* EMIT SIGNAL */
1627 _plugin_insert.reset ();
1628 _show_presets = false;
1629 processor_connections.drop_connections ();
1630 _showing_well_known = 0;
1631 notify_plugin_active_changed ();
1634 /* functor for FP8Strip's select button */
1636 FaderPort8::select_strip (boost::weak_ptr<Stripable> ws)
1638 boost::shared_ptr<Stripable> s = ws.lock();
1642 #if 1 /* single exclusive selection by default, toggle via shift */
1644 ToggleStripableSelection (s);
1646 SetStripableSelection (s);
1649 /* tri-state selection: This allows to set the "first selected"
1650 * with a single click without clearing the selection.
1651 * Single de/select via shift.
1654 if (s->is_selected ()) {
1655 RemoveStripableFromSelection (s);
1657 SetStripableSelection (s);
1661 if (s->is_selected () && s != first_selected_stripable ()) {
1662 set_first_selected_stripable (s);
1663 stripable_selection_changed ();
1665 ToggleStripableSelection (s);
1670 /* ****************************************************************************
1671 * Assigned Stripable Callbacks
1675 FaderPort8::notify_fader_mode_changed ()
1677 FaderMode fadermode = _ctrls.fader_mode ();
1679 boost::shared_ptr<Stripable> s = first_selected_stripable();
1680 if (!s && (fadermode == ModePlugins || fadermode == ModeSend)) {
1681 _ctrls.set_fader_mode (ModeTrack);
1685 drop_ctrl_connections ();
1687 switch (fadermode) {
1696 // force unset rec-arm button, see also FaderPort8::button_arm
1697 _ctrls.button (FP8Controls::BtnArm).set_active (false);
1698 ARMButtonChange (false);
1702 notify_automation_mode_changed ();
1706 FaderPort8::notify_stripable_added_or_removed ()
1710 * - session->RouteAdded
1711 * - PresentationInfo::Change
1712 * - Properties::hidden
1713 * - Properties::order
1718 /* called from static PresentationInfo::Change */
1720 FaderPort8::notify_pi_property_changed (const PropertyChange& what_changed)
1722 if (what_changed.contains (Properties::hidden)) {
1723 notify_stripable_added_or_removed ();
1725 if (what_changed.contains (Properties::order)) {
1726 notify_stripable_added_or_removed ();
1728 // Properties::selected is handled via StripableSelectionChanged
1732 FaderPort8::notify_stripable_property_changed (boost::weak_ptr<Stripable> ws, const PropertyChange& what_changed)
1734 boost::shared_ptr<Stripable> s = ws.lock();
1736 assert (0); // this should not happen
1739 if (_assigned_strips.find (s) == _assigned_strips.end()) {
1740 /* it can happen that signal emission is delayed.
1741 * A signal may already be in the queue but the
1742 * _assigned_strips has meanwhile changed.
1744 * before _assigned_strips changes, the connections are dropped
1745 * but that does not seem to invalidate pending requests :(
1747 * Seen when creating a new MB session and Mixbusses are added
1752 uint8_t id = _assigned_strips[s];
1754 if (what_changed.contains (Properties::color)) {
1755 _ctrls.strip(id).select_button ().set_color (s->presentation_info ().color());
1758 if (what_changed.contains (Properties::name)) {
1759 switch (_ctrls.fader_mode ()) {
1761 _ctrls.strip(id).set_text_line (3, s->name(), true);
1765 _ctrls.strip(id).set_text_line (0, s->name());
1775 FaderPort8::stripable_selection_changed ()
1777 if (!_device_active) {
1778 /* this can be called anytime from the static
1779 * ControlProtocol::StripableSelectionChanged
1783 automation_state_connections.drop_connections();
1785 switch (_ctrls.fader_mode ()) {
1787 if (_proc_params.size () > 0 && _showing_well_known < 0) {
1788 /* w/well-known -> re-assign to new strip */
1789 int wk = _showing_well_known;
1790 drop_ctrl_connections ();
1803 /* update selection lights */
1804 for (StripAssignmentMap::const_iterator i = _assigned_strips.begin(); i != _assigned_strips.end(); ++i) {
1805 boost::shared_ptr<ARDOUR::Stripable> s = i->first;
1806 uint8_t id = i->second;
1807 bool sel = s->is_selected ();
1808 _ctrls.strip(id).select_button ().set_active (sel);
1809 _ctrls.strip(id).select_button ().set_blinking (sel && s == first_selected_stripable ());
1812 /* track automation-mode of primary selection */
1813 boost::shared_ptr<Stripable> s = first_selected_stripable();
1815 boost::shared_ptr<AutomationControl> ac;
1816 ac = s->gain_control();
1817 if (ac && ac->alist()) {
1818 ac->alist()->automation_state_changed.connect (automation_state_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_automation_mode_changed, this), this);
1820 ac = s->pan_azimuth_control();
1821 if (ac && ac->alist()) {
1822 ac->alist()->automation_state_changed.connect (automation_state_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_automation_mode_changed, this), this);
1826 notify_automation_mode_changed ();
1830 /* ****************************************************************************
1835 FaderPort8::move_selected_into_view ()
1837 boost::shared_ptr<Stripable> selected = first_selected_stripable ();
1842 StripableList strips;
1843 filter_stripables (strips);
1845 StripableList::iterator it = std::find (strips.begin(), strips.end(), selected);
1846 if (it == strips.end()) {
1849 int off = std::distance (strips.begin(), it);
1851 int channel_off = get_channel_off (_ctrls.mix_mode ());
1852 if (channel_off <= off && off < channel_off + 8) {
1856 if (channel_off > off) {
1859 channel_off = off - 7;
1861 set_channel_off (_ctrls.mix_mode (), channel_off);
1866 FaderPort8::select_prev_next (bool next)
1868 StripableList strips;
1869 filter_stripables (strips);
1871 boost::shared_ptr<Stripable> selected = first_selected_stripable ();
1873 if (strips.size() > 0) {
1875 SetStripableSelection (strips.front ());
1877 SetStripableSelection (strips.back ());
1884 boost::shared_ptr<Stripable> toselect;
1885 for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1886 if (*s == selected) {
1892 if (s != strips.end()) {
1903 if (found && toselect) {
1904 SetStripableSelection (toselect);
1909 FaderPort8::bank (bool down, bool page)
1911 int dt = page ? 8 : 1;
1915 set_channel_off (_ctrls.mix_mode (), get_channel_off (_ctrls.mix_mode ()) + dt);
1920 FaderPort8::bank_param (bool down, bool page)
1922 int dt = page ? 8 : 1;
1926 switch (_ctrls.fader_mode ()) {
1928 if (_proc_params.size() > 0) {
1929 _parameter_off += dt;
1930 assign_processor_ctrls ();