2 * Copyright (C) 2017-2018 Ben Loftis <ben@harrisonconsoles.com>
3 * Copyright (C) 2017-2018 Robin Gareus <robin@gareus.org>
4 * Copyright (C) 2017 Paul Davis <paul@linuxaudiosystems.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "pbd/error.h"
28 #include "pbd/failed_constructor.h"
29 #include "pbd/pthread_utils.h"
30 #include "pbd/compose.h"
31 #include "pbd/xml++.h"
33 #include "midi++/port.h"
35 #include "ardour/audioengine.h"
36 #include "ardour/audio_track.h"
37 #include "ardour/bundle.h"
38 #include "ardour/debug.h"
39 #include "ardour/midi_track.h"
40 #include "ardour/midiport_manager.h"
41 #include "ardour/panner_shell.h"
42 #include "ardour/plugin.h"
43 #include "ardour/plugin_insert.h"
44 #include "ardour/processor.h"
45 #include "ardour/rc_configuration.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/session_configuration.h"
49 #include "ardour/tempo.h"
50 #include "ardour/vca.h"
52 #include "faderport8.h"
54 using namespace ARDOUR;
58 using namespace ArdourSurface::FP_NAMESPACE;
59 using namespace ArdourSurface::FP_NAMESPACE::FP8Types;
63 #include "pbd/abstract_ui.cc" // instantiate template
66 //#define VERBOSE_DEBUG
70 debug_2byte_msg (std::string const& msg, int b0, int b1)
73 if (DEBUG_ENABLED(DEBUG::FaderPort8)) {
75 DEBUG_STR_APPEND(a, "RECV: ");
76 DEBUG_STR_APPEND(a, msg);
77 DEBUG_STR_APPEND(a,' ');
78 DEBUG_STR_APPEND(a,hex);
79 DEBUG_STR_APPEND(a,"0x");
80 DEBUG_STR_APPEND(a, b0);
81 DEBUG_STR_APPEND(a,' ');
82 DEBUG_STR_APPEND(a,"0x");
83 DEBUG_STR_APPEND(a, b1);
84 DEBUG_STR_APPEND(a,'\n');
85 DEBUG_TRACE (DEBUG::FaderPort8, DEBUG_STR(a).str());
90 FaderPort8::FaderPort8 (Session& s)
92 : ControlProtocol (s, _("PreSonus FaderPort16"))
93 #elif defined FADERPORT2
94 : ControlProtocol (s, _("PreSonus FaderPort2"))
96 : ControlProtocol (s, _("PreSonus FaderPort8"))
98 , AbstractUI<FaderPort8Request> (name())
99 , _connection_state (ConnectionState (0))
100 , _device_active (false)
104 , _show_presets (false)
105 , _showing_well_known (0)
107 , _blink_onoff (false)
108 , _shift_lock (false)
111 , _link_enabled (false)
112 , _link_locked (false)
113 , _chan_locked (false)
116 , _two_line_text (false)
117 , _auto_pluginui (true)
119 boost::shared_ptr<ARDOUR::Port> inp;
120 boost::shared_ptr<ARDOUR::Port> outp;
123 inp = AudioEngine::instance()->register_input_port (DataType::MIDI, "FaderPort16 Recv", true);
124 outp = AudioEngine::instance()->register_output_port (DataType::MIDI, "FaderPort16 Send", true);
125 #elif defined FADERPORT2
126 inp = AudioEngine::instance()->register_input_port (DataType::MIDI, "FaderPort2 Recv", true);
127 outp = AudioEngine::instance()->register_output_port (DataType::MIDI, "FaderPort2 Send", true);
129 inp = AudioEngine::instance()->register_input_port (DataType::MIDI, "FaderPort8 Recv", true);
130 outp = AudioEngine::instance()->register_output_port (DataType::MIDI, "FaderPort8 Send", true);
132 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(inp);
133 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(outp);
135 if (_input_port == 0 || _output_port == 0) {
136 throw failed_constructor();
140 _input_bundle.reset (new ARDOUR::Bundle (_("FaderPort16 (Receive)"), true));
141 _output_bundle.reset (new ARDOUR::Bundle (_("FaderPort16 (Send)"), false));
142 #elif defined FADERPORT2
143 _input_bundle.reset (new ARDOUR::Bundle (_("FaderPort2 (Receive)"), true));
144 _output_bundle.reset (new ARDOUR::Bundle (_("FaderPort2 (Send)"), false));
146 _input_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Receive)"), true));
147 _output_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Send)"), false));
150 _input_bundle->add_channel (
152 ARDOUR::DataType::MIDI,
153 session->engine().make_port_name_non_relative (inp->name())
156 _output_bundle->add_channel (
158 ARDOUR::DataType::MIDI,
159 session->engine().make_port_name_non_relative (outp->name())
162 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::connection_handler, this, _2, _4), this);
163 ARDOUR::AudioEngine::instance()->Stopped.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::engine_reset, this), this);
164 ARDOUR::Port::PortDrop.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::engine_reset, this), this);
166 /* bind button events to call libardour actions */
169 _ctrls.FaderModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::notify_fader_mode_changed, this));
170 _ctrls.MixModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::assign_strips, this));
173 FaderPort8::~FaderPort8 ()
175 /* this will be called from the main UI thread. during Session::destroy().
176 * There can be concurrent activity from BaseUI::main_thread -> AsyncMIDIPort
177 * -> MIDI::Parser::signal -> ... to any of the midi_connections
179 * stop event loop early and join thread */
183 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
184 Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
185 AudioEngine::instance()->unregister_port (_input_port);
186 _input_port.reset ();
189 disconnected (); // zero faders, turn lights off, clear strips
192 _output_port->drain (10000, 250000); /* check every 10 msecs, wait up to 1/4 second for the port to drain */
193 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering output port %1\n", boost::shared_ptr<ARDOUR::Port>(_output_port)->name()));
194 Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
195 AudioEngine::instance()->unregister_port (_output_port);
196 _output_port.reset ();
202 /* ****************************************************************************
207 FaderPort8::request_factory (uint32_t num_requests)
209 /* AbstractUI<T>::request_buffer_factory() is a template method only
210 * instantiated in this source module. To provide something visible for
211 * use in the interface/descriptor, we have this static method that is
214 return request_buffer_factory (num_requests);
218 FaderPort8::do_request (FaderPort8Request* req)
220 if (req->type == CallSlot) {
221 call_slot (MISSING_INVALIDATOR, req->the_slot);
222 } else if (req->type == Quit) {
231 DEBUG_TRACE (DEBUG::FaderPort8, "BaseUI::quit ()\n");
233 close (); // drop references, disconnect from session signals
237 FaderPort8::thread_init ()
239 pthread_set_name (event_loop_name().c_str());
241 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
242 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
244 set_thread_priority ();
248 FaderPort8::periodic ()
250 /* prepare TC display -- handled by stripable Periodic ()
251 * in FP8Strip::periodic_update_timecode
253 if (_ctrls.display_timecode () && clock_mode ()) {
255 session->timecode_time (TC);
256 _timecode = Timecode::timecode_format_time(TC);
259 Timecode::BBT_Time BBT = session->tempo_map ().bbt_at_sample (session->transport_sample ());
260 snprintf (buf, sizeof (buf),
261 " %02" PRIu32 "|%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32,
262 BBT.bars % 100, BBT.beats %100,
263 (BBT.ticks/ 100) %100, BBT.ticks %100);
264 _musical_time = std::string (buf);
267 _musical_time.clear ();
271 /* every second, send "running" */
272 if (++_timer_divider == 10) {
274 tx_midi3 (0xa0, 0x00, 0x00);
278 /* update stripables */
284 FaderPort8::blink_it ()
286 _blink_onoff = !_blink_onoff;
287 BlinkIt (_blink_onoff);
291 /* ****************************************************************************
292 * Port and Signal Connection Management
295 FaderPort8::set_active (bool yn)
297 DEBUG_TRACE (DEBUG::FaderPort8, string_compose("set_active init with yn: '%1'\n", yn));
299 if (yn == active()) {
304 /* start event loop */
306 connect_session_signals ();
311 ControlProtocol::set_active (yn);
312 DEBUG_TRACE (DEBUG::FaderPort8, string_compose("set_active done with yn: '%1'\n", yn));
319 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::close\n");
320 stop_midi_handling ();
321 session_connections.drop_connections ();
322 route_state_connections.drop_connections ();
323 assigned_stripable_connections.drop_connections ();
324 _assigned_strips.clear ();
325 drop_ctrl_connections ();
326 port_connections.drop_connections ();
327 selection_connection.disconnect ();
331 FaderPort8::stop_midi_handling ()
333 _periodic_connection.disconnect ();
334 _blink_connection.disconnect ();
335 midi_connections.drop_connections ();
336 /* Note: the input handler is still active at this point, but we're no
337 * longer connected to any of the parser signals
342 FaderPort8::connected ()
344 DEBUG_TRACE (DEBUG::FaderPort8, "initializing\n");
345 assert (!_device_active);
347 if (_device_active) {
348 stop_midi_handling (); // re-init
351 // ideally check firmware version >= 1.01 (USB bcdDevice 0x0101) (vendor 0x194f prod 0x0202)
352 // but we don't have a handle to the underlying USB device here.
354 memset (_channel_off, 0, sizeof (_channel_off));
355 _plugin_off = _parameter_off = 0;
356 _blink_onoff = false;
361 start_midi_handling ();
362 _ctrls.initialize ();
364 /* highlight bound user-actions */
365 for (FP8Controls::UserButtonMap::const_iterator i = _ctrls.user_buttons ().begin ();
366 i != _ctrls.user_buttons ().end (); ++i) {
367 _ctrls.button (i->first).set_active (! _user_action_map[i->first].empty ());
369 /* shift button lights */
370 tx_midi3 (0x90, 0x06, 0x00);
371 tx_midi3 (0x90, 0x46, 0x00);
373 send_session_state ();
376 Glib::RefPtr<Glib::TimeoutSource> blink_timer =
377 Glib::TimeoutSource::create (200);
378 _blink_connection = blink_timer->connect (sigc::mem_fun (*this, &FaderPort8::blink_it));
379 blink_timer->attach (main_loop()->get_context());
381 Glib::RefPtr<Glib::TimeoutSource> periodic_timer =
382 Glib::TimeoutSource::create (100);
383 _periodic_connection = periodic_timer->connect (sigc::mem_fun (*this, &FaderPort8::periodic));
384 periodic_timer->attach (main_loop()->get_context());
388 FaderPort8::disconnected ()
390 stop_midi_handling ();
391 if (_device_active) {
392 for (uint8_t id = 0; id < N_STRIPS; ++id) {
393 _ctrls.strip(id).unset_controllables ();
395 _ctrls.all_lights_off ();
400 FaderPort8::engine_reset ()
402 /* Port::PortDrop is called when the engine is halted or stopped */
403 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::engine_reset\n");
404 _connection_state = 0;
405 _device_active = false;
410 FaderPort8::connection_handler (std::string name1, std::string name2)
413 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::connection_handler: start\n");
415 if (!_input_port || !_output_port) {
419 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
420 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
422 if (ni == name1 || ni == name2) {
423 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connection notify %1 and %2\n", name1, name2));
424 if (_input_port->connected ()) {
425 if (_connection_state & InputConnected) {
428 _connection_state |= InputConnected;
430 _connection_state &= ~InputConnected;
432 } else if (no == name1 || no == name2) {
433 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connection notify %1 and %2\n", name1, name2));
434 if (_output_port->connected ()) {
435 if (_connection_state & OutputConnected) {
438 _connection_state |= OutputConnected;
440 _connection_state &= ~OutputConnected;
444 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
450 if ((_connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
452 /* XXX this is a horrible hack. Without a short sleep here,
453 * something prevents the device wakeup messages from being
454 * sent and/or the responses from being received.
457 DEBUG_TRACE (DEBUG::FaderPort8, "device now connected for both input and output\n");
459 _device_active = true;
462 DEBUG_TRACE (DEBUG::FaderPort8, "Device disconnected (input or output or both) or not yet fully connected\n");
463 if (_device_active) {
466 _device_active = false;
469 ConnectionChange (); /* emit signal for our GUI */
472 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::connection_handler: end\n");
475 return true; /* connection status changed */
478 list<boost::shared_ptr<ARDOUR::Bundle> >
479 FaderPort8::bundles ()
481 list<boost::shared_ptr<ARDOUR::Bundle> > b;
484 b.push_back (_input_bundle);
485 b.push_back (_output_bundle);
491 /* ****************************************************************************
495 FaderPort8::midi_input_handler (Glib::IOCondition ioc, boost::weak_ptr<ARDOUR::AsyncMIDIPort> wport)
497 boost::shared_ptr<AsyncMIDIPort> port (wport.lock());
499 if (!port || !_input_port) {
504 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("something happend on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
515 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("data available on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
517 samplepos_t now = session->engine().sample_time();
525 FaderPort8::start_midi_handling ()
527 _input_port->parser()->sysex.connect_same_thread (midi_connections, boost::bind (&FaderPort8::sysex_handler, this, _1, _2, _3));
528 _input_port->parser()->poly_pressure.connect_same_thread (midi_connections, boost::bind (&FaderPort8::polypressure_handler, this, _1, _2));
529 for (uint8_t i = 0; i < 16; ++i) {
530 _input_port->parser()->channel_pitchbend[i].connect_same_thread (midi_connections, boost::bind (&FaderPort8::pitchbend_handler, this, _1, i, _2));
532 _input_port->parser()->controller.connect_same_thread (midi_connections, boost::bind (&FaderPort8::controller_handler, this, _1, _2));
533 _input_port->parser()->note_on.connect_same_thread (midi_connections, boost::bind (&FaderPort8::note_on_handler, this, _1, _2));
534 _input_port->parser()->note_off.connect_same_thread (midi_connections, boost::bind (&FaderPort8::note_off_handler, this, _1, _2));
536 /* This connection means that whenever data is ready from the input
537 * port, the relevant thread will invoke our ::midi_input_handler()
538 * method, which will read the data, and invoke the parser.
540 _input_port->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &FaderPort8::midi_input_handler), boost::weak_ptr<AsyncMIDIPort> (_input_port)));
541 _input_port->xthread().attach (main_loop()->get_context());
545 FaderPort8::tx_midi (std::vector<uint8_t> const& d) const
547 /* work around midi buffer overflow for batch changes */
548 if (d.size() == 3 && (d[0] == 0x91 || d[0] == 0x92)) {
549 /* set colors triplet in one go */
550 } else if (d.size() == 3 && (d[0] == 0x93)) {
553 g_usleep (400 * d.size());
556 size_t tx = _output_port->write (&d[0], d.size(), 0);
557 assert (tx == d.size());
560 return _output_port->write (&d[0], d.size(), 0);
564 /* ****************************************************************************
568 FaderPort8::polypressure_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
570 debug_2byte_msg ("PP", tb->controller_number, tb->value);
571 // outgoing only (meter)
575 FaderPort8::pitchbend_handler (MIDI::Parser &, uint8_t chan, MIDI::pitchbend_t pb)
577 debug_2byte_msg ("PB", chan, pb);
578 /* fader 0..16368 (0x3ff0 -- 1024 steps) */
579 bool handled = _ctrls.midi_fader (chan, pb);
580 /* if Shift key is held while moving a fader (group override), don't lock shift. */
581 if ((_shift_pressed > 0) && handled) {
582 _shift_connection.disconnect ();
588 FaderPort8::controller_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
590 debug_2byte_msg ("CC", tb->controller_number, tb->value);
592 * val Bit 6 = direction, Bits 0-5 = number of steps
594 static const uint8_t dir_mask = 0x40;
595 static const uint8_t step_mask = 0x3f;
597 if (tb->controller_number == 0x3c) {
598 encoder_navigate (tb->value & dir_mask ? true : false, tb->value & step_mask);
600 if (tb->controller_number == 0x10) {
602 if (_ctrls.nav_mode() == NavPan) {
603 encoder_parameter (tb->value & dir_mask ? true : false, tb->value & step_mask);
605 encoder_navigate (tb->value & dir_mask ? true : false, tb->value & step_mask);
608 encoder_parameter (tb->value & dir_mask ? true : false, tb->value & step_mask);
614 FaderPort8::note_on_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
616 debug_2byte_msg ("ON", tb->note_number, tb->velocity);
620 static const uint8_t touch_id_uppper = 0x77;
622 static const uint8_t touch_id_uppper = 0x6f;
624 if (tb->note_number >= 0x68 && tb->note_number <= touch_id_uppper) {
625 _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
629 /* special case shift */
630 if (tb->note_number == 0x06 || tb->note_number == 0x46) {
631 _shift_pressed |= (tb->note_number == 0x06) ? 1 : 2;
632 if (_shift_pressed == 3) {
635 _shift_connection.disconnect ();
638 ShiftButtonChange (false);
639 tx_midi3 (0x90, 0x06, 0x00);
640 tx_midi3 (0x90, 0x46, 0x00);
644 Glib::RefPtr<Glib::TimeoutSource> shift_timer =
645 Glib::TimeoutSource::create (1000);
646 shift_timer->attach (main_loop()->get_context());
647 _shift_connection = shift_timer->connect (sigc::mem_fun (*this, &FaderPort8::shift_timeout));
649 ShiftButtonChange (true);
650 tx_midi3 (0x90, 0x06, 0x7f);
651 tx_midi3 (0x90, 0x46, 0x7f);
655 _ctrls.midi_event (tb->note_number, tb->velocity);
659 FaderPort8::note_off_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
661 debug_2byte_msg ("OF", tb->note_number, tb->velocity);
664 static const uint8_t touch_id_uppper = 0x77;
666 static const uint8_t touch_id_uppper = 0x6f;
668 if (tb->note_number >= 0x68 && tb->note_number <= touch_id_uppper) {
670 _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
674 /* special case shift */
675 if (tb->note_number == 0x06 || tb->note_number == 0x46) {
676 _shift_pressed &= (tb->note_number == 0x06) ? 2 : 1;
677 if (_shift_pressed > 0) {
683 ShiftButtonChange (false);
684 tx_midi3 (0x90, 0x06, 0x00);
685 tx_midi3 (0x90, 0x46, 0x00);
686 /* just in case this happens concurrently */
687 _shift_connection.disconnect ();
692 bool handled = _ctrls.midi_event (tb->note_number, tb->velocity);
693 /* if Shift key is held while activating an action, don't lock shift. */
694 if ((_shift_pressed > 0) && handled) {
695 _shift_connection.disconnect ();
701 FaderPort8::sysex_handler (MIDI::Parser &p, MIDI::byte *buf, size_t size)
704 if (DEBUG_ENABLED(DEBUG::FaderPort8)) {
706 DEBUG_STR_APPEND(a, string_compose ("RECV sysex siz=%1", size));
707 for (size_t i=0; i < size; ++i) {
708 DEBUG_STR_APPEND(a,hex);
709 DEBUG_STR_APPEND(a,"0x");
710 DEBUG_STR_APPEND(a,(int)buf[i]);
711 DEBUG_STR_APPEND(a,' ');
713 DEBUG_STR_APPEND(a,'\n');
714 DEBUG_TRACE (DEBUG::FaderPort8, DEBUG_STR(a).str());
719 /* ****************************************************************************
723 FaderPort8::set_button_action (FP8Controls::ButtonId id, bool press, std::string const& action_name)
725 if (_ctrls.user_buttons().find (id) == _ctrls.user_buttons().end ()) {
728 _user_action_map[id].action (press).assign_action (action_name);
730 if (!_device_active) {
733 _ctrls.button (id).set_active (!_user_action_map[id].empty ());
737 FaderPort8::get_button_action (FP8Controls::ButtonId id, bool press)
739 return _user_action_map[id].action(press)._action_name;
742 /* ****************************************************************************
746 FaderPort8::get_state ()
748 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::get_state\n");
749 XMLNode& node (ControlProtocol::get_state());
753 child = new XMLNode (X_("Input"));
754 child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_input_port)->get_state());
755 node.add_child_nocopy (*child);
757 child = new XMLNode (X_("Output"));
758 child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_output_port)->get_state());
759 node.add_child_nocopy (*child);
762 node.set_property (X_("clock-mode"), _clock_mode);
763 node.set_property (X_("scribble-mode"), _scribble_mode);
764 node.set_property (X_("two-line-text"), _two_line_text);
767 for (UserActionMap::const_iterator i = _user_action_map.begin (); i != _user_action_map.end (); ++i) {
768 if (i->second.empty()) {
772 if (!_ctrls.button_enum_to_name (i->first, name)) {
775 XMLNode* btn = new XMLNode (X_("Button"));
776 btn->set_property (X_("id"), name);
777 if (!i->second.action(true).empty ()) {
778 btn->set_property ("press", i->second.action(true)._action_name);
780 if (!i->second.action(false).empty ()) {
781 btn->set_property ("release", i->second.action(false)._action_name);
783 node.add_child_nocopy (*btn);
790 FaderPort8::set_state (const XMLNode& node, int version)
792 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state\n");
794 XMLNodeConstIterator niter;
795 XMLNode const* child;
797 if (ControlProtocol::set_state (node, version)) {
801 if ((child = node.child (X_("Input"))) != 0) {
802 XMLNode* portnode = child->child (Port::state_node_name.c_str());
804 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state Input\n");
805 boost::shared_ptr<ARDOUR::Port>(_input_port)->set_state (*portnode, version);
809 if ((child = node.child (X_("Output"))) != 0) {
810 XMLNode* portnode = child->child (Port::state_node_name.c_str());
812 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state Output\n");
813 boost::shared_ptr<ARDOUR::Port>(_output_port)->set_state (*portnode, version);
817 node.get_property (X_("clock-mode"), _clock_mode);
818 node.get_property (X_("scribble-mode"), _scribble_mode);
819 node.get_property (X_("two-line-text"), _two_line_text);
821 _user_action_map.clear ();
822 // TODO: When re-loading state w/o surface re-init becomes possible,
823 // unset lights and reset colors of user buttons.
825 for (XMLNodeList::const_iterator n = node.children().begin(); n != node.children().end(); ++n) {
826 if ((*n)->name() != X_("Button")) {
831 if (!(*n)->get_property (X_("id"), id_str)) {
835 FP8Controls::ButtonId id;
836 if (!_ctrls.button_name_to_enum (id_str, id)) {
840 std::string action_str;
841 if ((*n)->get_property (X_("press"), action_str)) {
842 set_button_action (id, true, action_str);
844 if ((*n)->get_property (X_("release"), action_str)) {
845 set_button_action (id, false, action_str);
852 /* ****************************************************************************
853 * Stripable Assignment
856 static bool flt_audio_track (boost::shared_ptr<Stripable> s) {
857 return boost::dynamic_pointer_cast<AudioTrack>(s) != 0;
860 static bool flt_midi_track (boost::shared_ptr<Stripable> s) {
861 return boost::dynamic_pointer_cast<MidiTrack>(s) != 0;
864 static bool flt_bus (boost::shared_ptr<Stripable> s) {
865 if (boost::dynamic_pointer_cast<Route>(s) == 0) {
869 if (s->mixbus () == 0) {
873 return boost::dynamic_pointer_cast<Track>(s) == 0;
876 static bool flt_auxbus (boost::shared_ptr<Stripable> s) {
877 if (boost::dynamic_pointer_cast<Route>(s) == 0) {
881 if (s->mixbus () > 0) {
885 return boost::dynamic_pointer_cast<Track>(s) == 0;
888 static bool flt_vca (boost::shared_ptr<Stripable> s) {
889 return boost::dynamic_pointer_cast<VCA>(s) != 0;
892 static bool flt_selected (boost::shared_ptr<Stripable> s) {
893 return s->is_selected ();
896 static bool flt_mains (boost::shared_ptr<Stripable> s) {
897 return (s->is_master() || s->is_monitor());
900 static bool flt_all (boost::shared_ptr<Stripable> s) {
904 static bool flt_rec_armed (boost::shared_ptr<Stripable> s) {
905 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
909 return t->rec_enable_control ()->get_value () > 0.;
912 static bool flt_instrument (boost::shared_ptr<Stripable> s) {
913 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(s);
917 return 0 != r->the_instrument ();
921 FaderPort8::filter_stripables (StripableList& strips) const
923 typedef bool (*FilterFunction)(boost::shared_ptr<Stripable>);
926 bool allow_master = false;
927 bool allow_monitor = false;
929 switch (_ctrls.mix_mode ()) {
931 flt = &flt_audio_track;
934 flt = &flt_instrument;
943 flt = &flt_midi_track;
951 allow_monitor = true;
955 flt = &flt_rec_armed;
970 session->get_stripables (all);
972 for (StripableList::const_iterator s = all.begin(); s != all.end(); ++s) {
973 if ((*s)->is_auditioner ()) { continue; }
974 if ((*s)->is_hidden ()) { continue; }
976 if (!allow_master && (*s)->is_master ()) { continue; }
977 if (!allow_monitor && (*s)->is_monitor ()) { continue; }
980 strips.push_back (*s);
983 strips.sort (Stripable::Sorter(true));
986 /* Track/Pan mode: assign stripable to strips, Send-mode: selection */
988 FaderPort8::assign_stripables (bool select_only)
990 StripableList strips;
991 filter_stripables (strips);
994 set_periodic_display_mode (FP8Strip::Stripables);
998 boost::shared_ptr<Stripable> s = first_selected_stripable();
1000 _ctrls.strip(0).set_stripable (s, _ctrls.fader_mode() == ModePan);
1002 _ctrls.strip(0).unset_controllables ( FP8Strip::CTRL_ALL );
1007 int n_strips = strips.size();
1008 int channel_off = get_channel_off (_ctrls.mix_mode ());
1009 channel_off = std::min (channel_off, n_strips - N_STRIPS);
1010 channel_off = std::max (0, channel_off);
1011 set_channel_off (_ctrls.mix_mode (), channel_off);
1014 int skip = channel_off;
1015 for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1021 _assigned_strips[*s] = id;
1022 (*s)->DropReferences.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
1023 boost::bind (&FaderPort8::notify_stripable_added_or_removed, this), this);
1025 (*s)->PropertyChanged.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
1026 boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
1027 (*s)->presentation_info ().PropertyChanged.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
1028 boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
1030 if (boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(*s)) {
1031 if (r->panner_shell()) {
1032 r->panner_shell()->Changed.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
1033 boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), PropertyChange()), this);
1038 /* used in send mode */
1039 _ctrls.strip(id).set_text_line (3, (*s)->name (), true);
1040 _ctrls.strip(id).set_select_button_color ((*s)->presentation_info ().color());
1041 /* update selection lights */
1042 _ctrls.strip(id).select_button ().set_active ((*s)->is_selected ());
1043 _ctrls.strip(id).select_button ().set_blinking (*s == first_selected_stripable ());
1045 _ctrls.strip(id).set_stripable (*s, _ctrls.fader_mode() == ModePan);
1048 boost::function<void ()> cb (boost::bind (&FaderPort8::select_strip, this, boost::weak_ptr<Stripable> (*s)));
1049 _ctrls.strip(id).set_select_cb (cb);
1051 if (++id == N_STRIPS) {
1055 for (; id < N_STRIPS; ++id) {
1056 _ctrls.strip(id).unset_controllables (select_only ? (FP8Strip::CTRL_SELECT | FP8Strip::CTRL_TEXT3) : FP8Strip::CTRL_ALL);
1057 _ctrls.strip(id).set_periodic_display_mode (FP8Strip::Stripables);
1061 /* ****************************************************************************
1066 FaderPort8::unlock_link (bool drop)
1068 link_locked_connection.disconnect ();
1071 stop_link (); // calls back here with drop = false
1075 _link_locked = false;
1077 if (_link_enabled) {
1078 assert (_ctrls.button (FP8Controls::BtnLink).is_active ());
1079 _link_control.reset ();
1080 start_link (); // re-connect & update LED colors
1082 _ctrls.button (FP8Controls::BtnLink).set_active (false);
1083 _ctrls.button (FP8Controls::BtnLink).set_color (0x888888ff);
1084 _ctrls.button (FP8Controls::BtnLock).set_active (false);
1085 _ctrls.button (FP8Controls::BtnLock).set_color (0x888888ff);
1090 FaderPort8::lock_link ()
1092 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (_link_control.lock ());
1096 ac->DropReferences.connect (link_locked_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::unlock_link, this, true), this);
1098 // stop watching for focus events
1099 link_connection.disconnect ();
1101 _link_locked = true;
1103 _ctrls.button (FP8Controls::BtnLock).set_color (0x00ff00ff);
1104 _ctrls.button (FP8Controls::BtnLink).set_color (0x00ff00ff);
1108 FaderPort8::stop_link ()
1110 if (!_link_enabled) {
1113 link_connection.disconnect ();
1114 _link_control.reset ();
1115 _link_enabled = false;
1116 unlock_link (); // also updates button colors
1120 FaderPort8::start_link ()
1122 assert (!_link_locked);
1124 _link_enabled = true;
1125 _ctrls.button (FP8Controls::BtnLink).set_active (true);
1126 _ctrls.button (FP8Controls::BtnLock).set_active (true);
1127 nofity_focus_control (_link_control); // update BtnLink, BtnLock colors
1129 PBD::Controllable::GUIFocusChanged.connect (link_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::nofity_focus_control, this, _1), this);
1133 /* ****************************************************************************
1134 * Plugin selection and parameters
1138 FaderPort8::toggle_preset_param_mode ()
1140 FaderMode fadermode = _ctrls.fader_mode ();
1141 if (fadermode != ModePlugins || _proc_params.size() == 0) {
1144 _show_presets = ! _show_presets;
1145 assign_processor_ctrls ();
1149 FaderPort8::preset_changed ()
1151 if (_show_presets) {
1152 assign_processor_ctrls ();
1157 FaderPort8::assign_processor_ctrls ()
1159 if (_proc_params.size() == 0) {
1160 _ctrls.set_fader_mode (ModeTrack);
1163 set_periodic_display_mode (FP8Strip::PluginParam);
1165 if (_show_presets) {
1166 if (assign_plugin_presets (_plugin_insert.lock ())) {
1169 _show_presets = false;
1172 std::vector <ProcessorCtrl*> toggle_params;
1173 std::vector <ProcessorCtrl*> slider_params;
1175 for ( std::list <ProcessorCtrl>::iterator i = _proc_params.begin(); i != _proc_params.end(); ++i) {
1176 if ((*i).ac->toggled()) {
1177 toggle_params.push_back (&(*i));
1179 slider_params.push_back (&(*i));
1183 int n_parameters = std::max (toggle_params.size(), slider_params.size());
1185 _parameter_off = std::min (_parameter_off, n_parameters - N_STRIPS);
1186 _parameter_off = std::max (0, _parameter_off);
1189 for (size_t i = _parameter_off; i < (size_t)n_parameters; ++i) {
1190 if (i >= toggle_params.size ()) {
1191 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT01 & ~FP8Strip::CTRL_TEXT2);
1193 else if (i >= slider_params.size ()) {
1194 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_SELECT & ~FP8Strip::CTRL_TEXT3);
1196 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1199 if (i < slider_params.size ()) {
1200 _ctrls.strip(id).set_fader_controllable (slider_params[i]->ac);
1201 std::string param_name = slider_params[i]->name;
1202 _ctrls.strip(id).set_text_line (0, param_name.substr (0, 9));
1203 _ctrls.strip(id).set_text_line (1, param_name.length () > 9 ? param_name.substr (9) : "");
1205 if (i < toggle_params.size ()) {
1206 _ctrls.strip(id).set_select_controllable (toggle_params[i]->ac);
1207 _ctrls.strip(id).set_text_line (3, toggle_params[i]->name, true);
1209 if (++id == N_STRIPS) {
1215 for (; id < N_STRIPS; ++id) {
1216 _ctrls.strip(id).unset_controllables ();
1221 FaderPort8::assign_plugin_presets (boost::shared_ptr<PluginInsert> pi)
1226 boost::shared_ptr<ARDOUR::Plugin> plugin = pi->plugin ();
1228 std::vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets ();
1229 if (presets.size () == 0) {
1233 int n_parameters = presets.size ();
1235 _parameter_off = std::min (_parameter_off, n_parameters - (N_STRIPS - 1));
1236 _parameter_off = std::max (0, _parameter_off);
1237 Plugin::PresetRecord active = plugin->last_preset ();
1240 for (size_t i = _parameter_off; i < (size_t)n_parameters; ++i) {
1241 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT01 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1242 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin_preset, this, i));
1243 _ctrls.strip(id).set_select_cb (cb);
1244 _ctrls.strip(id).select_button ().set_active (true);
1245 if (active != presets.at(i)) {
1246 _ctrls.strip(id).select_button ().set_color (0x0000ffff);
1247 _ctrls.strip(id).select_button ().set_blinking (false);
1249 _ctrls.strip(id).select_button ().set_color (0x00ffffff);
1250 _ctrls.strip(id).select_button ().set_blinking (plugin->parameter_changed_since_last_preset ());
1252 std::string label = presets.at(i).label;
1253 _ctrls.strip(id).set_text_line (0, label.substr (0, 9));
1254 _ctrls.strip(id).set_text_line (1, label.length () > 9 ? label.substr (9) : "");
1255 _ctrls.strip(id).set_text_line (3, "PRESET", true);
1256 if (++id == (N_STRIPS - 1)) {
1262 for (; id < (N_STRIPS - 1); ++id) {
1263 _ctrls.strip(id).unset_controllables ();
1266 // pin clear-preset to the last slot
1267 assert (id == (N_STRIPS - 1));
1268 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1269 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin_preset, this, SIZE_MAX));
1270 _ctrls.strip(id).set_select_cb (cb);
1271 _ctrls.strip(id).select_button ().set_blinking (false);
1272 _ctrls.strip(id).select_button ().set_color (active.uri.empty() ? 0x00ffffff : 0x0000ffff);
1273 _ctrls.strip(id).select_button ().set_active (true);
1274 _ctrls.strip(id).set_text_line (0, _("(none)"));
1275 _ctrls.strip(id).set_text_line (3, "PRESET", true);
1280 FaderPort8::build_well_known_processor_ctrls (boost::shared_ptr<Stripable> s, bool eq)
1282 #define PUSH_BACK_NON_NULL(N, C) do {if (C) { _proc_params.push_back (ProcessorCtrl (N, C)); }} while (0)
1284 _proc_params.clear ();
1286 int cnt = s->eq_band_cnt();
1289 PUSH_BACK_NON_NULL ("Flt In", s->filter_enable_controllable (true)); // both HP/LP
1290 PUSH_BACK_NON_NULL ("HP Freq", s->filter_freq_controllable (true));
1291 PUSH_BACK_NON_NULL ("LP Freq", s->filter_freq_controllable (false));
1292 PUSH_BACK_NON_NULL ("EQ In", s->eq_enable_controllable ());
1293 #elif defined (MIXBUS)
1294 PUSH_BACK_NON_NULL ("EQ In", s->eq_enable_controllable ());
1295 PUSH_BACK_NON_NULL ("HP Freq", s->filter_freq_controllable (true));
1298 for (int band = 0; band < cnt; ++band) {
1299 std::string bn = s->eq_band_name (band);
1300 PUSH_BACK_NON_NULL (string_compose ("Gain %1", bn), s->eq_gain_controllable (band));
1301 PUSH_BACK_NON_NULL (string_compose ("Freq %1", bn), s->eq_freq_controllable (band));
1302 PUSH_BACK_NON_NULL (string_compose ("Band %1", bn), s->eq_q_controllable (band));
1303 PUSH_BACK_NON_NULL (string_compose ("Shape %1", bn), s->eq_shape_controllable (band));
1306 PUSH_BACK_NON_NULL ("Comp In", s->comp_enable_controllable ());
1307 PUSH_BACK_NON_NULL ("Threshold", s->comp_threshold_controllable ());
1308 PUSH_BACK_NON_NULL ("Makeup", s->comp_makeup_controllable ());
1309 PUSH_BACK_NON_NULL ("Speed", s->comp_speed_controllable ());
1310 PUSH_BACK_NON_NULL ("Mode", s->comp_mode_controllable ());
1315 FaderPort8::select_plugin (int num)
1317 // make sure drop_ctrl_connections() was called
1318 assert (_proc_params.size() == 0 && _showing_well_known == 0 && _plugin_insert.expired());
1320 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (first_selected_stripable());
1322 _ctrls.set_fader_mode (ModeTrack);
1329 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (r->nth_plugin (num));
1331 if (pi && !pi->is_channelstrip () && pi->display_to_user ())
1333 if (pi && pi->display_to_user ())
1336 pi->enable (! pi->enabled ());
1343 build_well_known_processor_ctrls (r, num == -1);
1344 assign_processor_ctrls ();
1345 _showing_well_known = num;
1348 _showing_well_known = 0;
1350 boost::shared_ptr<Processor> proc = r->nth_plugin (num);
1352 _ctrls.set_fader_mode (ModeTrack);
1356 // disconnect signals from spill_plugins: processors_changed and ActiveChanged
1357 processor_connections.drop_connections ();
1358 r->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1360 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1361 assert (pi); // nth_plugin() always returns a PI.
1362 /* _plugin_insert is used for Bypass/Enable & presets */
1364 if (!pi->is_channelstrip () && pi->display_to_user ())
1366 if (pi->display_to_user ())
1369 _plugin_insert = boost::weak_ptr<ARDOUR::PluginInsert> (pi);
1370 pi->ActiveChanged.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_plugin_active_changed, this), this);
1371 boost::shared_ptr<ARDOUR::Plugin> plugin = pi->plugin ();
1373 plugin->PresetAdded.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
1374 plugin->PresetRemoved.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
1375 plugin->PresetLoaded.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
1376 plugin->PresetDirty.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
1378 if (_auto_pluginui) {
1379 pi->ShowUI (); /* EMIT SIGNAL */
1383 // switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed()
1384 // which drops the references, disconnects the signal and re-spills tracks
1385 proc->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1388 _proc_params.clear();
1389 set<Evoral::Parameter> p = proc->what_can_be_automated ();
1390 for (set<Evoral::Parameter>::iterator i = p.begin(); i != p.end(); ++i) {
1391 std::string n = proc->describe_parameter (*i);
1392 if (n == "hidden") {
1395 _proc_params.push_back (ProcessorCtrl (n, proc->automation_control (*i)));
1398 // TODO: open plugin GUI if (_proc_params.size() > 0)
1401 assign_processor_ctrls ();
1402 notify_plugin_active_changed ();
1406 FaderPort8::select_plugin_preset (size_t num)
1408 assert (_proc_params.size() > 0);
1409 boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock();
1411 _ctrls.set_fader_mode (ModeTrack);
1414 if (num == SIZE_MAX) {
1415 pi->plugin ()->clear_preset ();
1417 std::vector<ARDOUR::Plugin::PresetRecord> presets = pi->plugin ()->get_presets ();
1418 if (num < presets.size ()) {
1419 pi->load_preset (presets.at (num));
1422 _show_presets = false;
1423 assign_processor_ctrls ();
1426 /* short 4 chars at most */
1427 static std::string plugintype (ARDOUR::PluginType t) {
1444 return enum_2_string (t);
1448 FaderPort8::spill_plugins ()
1450 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (first_selected_stripable());
1452 _ctrls.set_fader_mode (ModeTrack);
1456 drop_ctrl_connections ();
1458 // switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed()
1459 // which drops the references, disconnects the signal and re-spills tracks
1460 r->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1462 // update when processor change
1463 r->processors_changed.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::spill_plugins, this), this);
1466 boost::shared_ptr<Processor> proc;
1468 std::vector<uint32_t> procs;
1470 for (uint32_t i = 0; 0 != (proc = r->nth_plugin (i)); ++i) {
1471 if (!proc->display_to_user ()) {
1475 /* don't show channelstrip plugins, use "well known" */
1476 if (boost::dynamic_pointer_cast<PluginInsert> (proc)->is_channelstrip ()) {
1481 set<Evoral::Parameter> p = proc->what_can_be_automated ();
1482 for (set<Evoral::Parameter>::iterator j = p.begin(); j != p.end(); ++j) {
1483 std::string n = proc->describe_parameter (*j);
1484 if (n == "hidden") {
1489 if (n_controls > 0) {
1490 procs.push_back (i);
1494 int n_plugins = procs.size();
1495 int spillwidth = N_STRIPS;
1496 bool have_well_known_eq = false;
1497 bool have_well_known_comp = false;
1499 // reserve last slot(s) for "well-known"
1500 if (r->eq_band_cnt() > 0) {
1502 have_well_known_eq = true;
1504 if (r->comp_enable_controllable ()) {
1506 have_well_known_comp = true;
1509 if (n_plugins == 0 && !have_well_known_eq && !have_well_known_comp) {
1510 _ctrls.set_fader_mode (ModeTrack);
1514 set_periodic_display_mode (FP8Strip::PluginSelect);
1516 _plugin_off = std::min (_plugin_off, n_plugins - spillwidth);
1517 _plugin_off = std::max (0, _plugin_off);
1520 for (uint32_t i = _plugin_off; ; ++i) {
1521 if (i >= procs.size()) {
1524 boost::shared_ptr<Processor> proc = r->nth_plugin (procs[i]);
1528 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1529 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, procs[i]));
1531 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1532 _ctrls.strip(id).set_select_cb (cb);
1533 _ctrls.strip(id).select_button ().set_color (proc->enabled () ? 0x00ff00ff : 0xff0000ff);
1534 _ctrls.strip(id).select_button ().set_active (true);
1535 _ctrls.strip(id).select_button ().set_blinking (false);
1536 _ctrls.strip(id).set_text_line (0, proc->name());
1537 _ctrls.strip(id).set_text_line (1, pi->plugin()->maker());
1538 _ctrls.strip(id).set_text_line (2, plugintype (pi->type()));
1539 _ctrls.strip(id).set_text_line (3, "");
1541 pi->ActiveChanged.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::spill_plugins, this), this);
1543 if (++id == spillwidth) {
1548 for (; id < spillwidth; ++id) {
1549 _ctrls.strip(id).unset_controllables ();
1552 if (have_well_known_comp) {
1553 assert (id < N_STRIPS);
1554 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -2));
1555 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1556 _ctrls.strip(id).set_select_cb (cb);
1557 _ctrls.strip(id).select_button ().set_color (0xffff00ff);
1558 _ctrls.strip(id).select_button ().set_active (true);
1559 _ctrls.strip(id).select_button ().set_blinking (false);
1560 _ctrls.strip(id).set_text_line (0, "Comp");
1561 _ctrls.strip(id).set_text_line (1, "Built-In");
1562 _ctrls.strip(id).set_text_line (2, "--");
1563 _ctrls.strip(id).set_text_line (3, "");
1566 if (have_well_known_eq) {
1567 assert (id < N_STRIPS);
1568 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -1));
1569 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1570 _ctrls.strip(id).set_select_cb (cb);
1571 _ctrls.strip(id).select_button ().set_color (0xffff00ff);
1572 _ctrls.strip(id).select_button ().set_active (true);
1573 _ctrls.strip(id).select_button ().set_blinking (false);
1574 _ctrls.strip(id).set_text_line (0, "EQ");
1575 _ctrls.strip(id).set_text_line (1, "Built-In");
1576 _ctrls.strip(id).set_text_line (2, "--");
1577 _ctrls.strip(id).set_text_line (3, "");
1580 assert (id == N_STRIPS);
1583 /* ****************************************************************************
1584 * Aux Sends and Mixbus assigns
1588 FaderPort8::assign_sends ()
1590 boost::shared_ptr<Stripable> s = first_selected_stripable();
1592 _ctrls.set_fader_mode (ModeTrack);
1597 while (0 != s->send_level_controllable (n_sends)) {
1601 _ctrls.set_fader_mode (ModeTrack);
1605 drop_ctrl_connections ();
1606 s->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1608 set_periodic_display_mode (FP8Strip::SendDisplay);
1610 _plugin_off = std::min (_plugin_off, n_sends - N_STRIPS);
1611 _plugin_off = std::max (0, _plugin_off);
1614 int skip = _parameter_off;
1615 for (uint32_t i = _plugin_off; ; ++i) {
1620 boost::shared_ptr<AutomationControl> send = s->send_level_controllable (i);
1625 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT01 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1626 _ctrls.strip(id).set_fader_controllable (send);
1627 _ctrls.strip(id).set_text_line (0, s->send_name (i));
1628 _ctrls.strip(id).set_mute_controllable (s->send_enable_controllable (i));
1630 if (++id == N_STRIPS) {
1635 for (; id < N_STRIPS; ++id) {
1636 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1638 #ifdef MIXBUS // master-assign on last solo
1639 _ctrls.strip(N_STRIPS - 1).set_solo_controllable (s->master_send_enable_controllable ());
1641 /* set select buttons */
1642 assigned_stripable_connections.drop_connections ();
1643 _assigned_strips.clear ();
1644 assign_stripables (true);
1647 /* ****************************************************************************
1648 * Main stripable assignment (dispatch depending on mode)
1652 FaderPort8::assign_strips ()
1654 assigned_stripable_connections.drop_connections ();
1655 _assigned_strips.clear ();
1657 FaderMode fadermode = _ctrls.fader_mode ();
1658 switch (fadermode) {
1661 assign_stripables ();
1662 stripable_selection_changed (); // update selection, automation-state
1665 if (_proc_params.size() > 0) {
1666 assign_processor_ctrls ();
1677 /* ****************************************************************************
1678 * some helper functions
1682 FaderPort8::set_periodic_display_mode (FP8Strip::DisplayMode m)
1684 for (uint8_t id = 0; id < N_STRIPS; ++id) {
1685 _ctrls.strip(id).set_periodic_display_mode (m);
1690 FaderPort8::drop_ctrl_connections ()
1692 _proc_params.clear();
1693 if (_auto_pluginui) {
1694 boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock ();
1696 pi->HideUI (); /* EMIT SIGNAL */
1699 _plugin_insert.reset ();
1700 _show_presets = false;
1701 processor_connections.drop_connections ();
1702 _showing_well_known = 0;
1703 notify_plugin_active_changed ();
1706 /* functor for FP8Strip's select button */
1708 FaderPort8::select_strip (boost::weak_ptr<Stripable> ws)
1710 boost::shared_ptr<Stripable> s = ws.lock();
1714 #if 1 /* single exclusive selection by default, toggle via shift */
1716 # if 1 /* selecting a selected strip -> move fader to unity */
1717 if (s == first_selected_stripable () && !shift_mod ()) {
1718 if (_ctrls.fader_mode () == ModeTrack) {
1719 boost::shared_ptr<AutomationControl> ac = s->gain_control ();
1720 ac->start_touch (ac->session().transport_sample());
1721 ac->set_value (ac->normal (), PBD::Controllable::UseGroup);
1728 ToggleStripableSelection (s);
1730 SetStripableSelection (s);
1733 /* tri-state selection: This allows to set the "first selected"
1734 * with a single click without clearing the selection.
1735 * Single de/select via shift.
1738 if (s->is_selected ()) {
1739 RemoveStripableFromSelection (s);
1741 SetStripableSelection (s);
1745 if (s->is_selected () && s != first_selected_stripable ()) {
1746 set_first_selected_stripable (s);
1747 stripable_selection_changed ();
1749 ToggleStripableSelection (s);
1754 /* ****************************************************************************
1755 * Assigned Stripable Callbacks
1759 FaderPort8::notify_fader_mode_changed ()
1761 FaderMode fadermode = _ctrls.fader_mode ();
1763 boost::shared_ptr<Stripable> s = first_selected_stripable();
1764 if (!s && (fadermode == ModePlugins || fadermode == ModeSend)) {
1765 _ctrls.set_fader_mode (ModeTrack);
1769 drop_ctrl_connections ();
1771 switch (fadermode) {
1780 // force unset rec-arm button, see also FaderPort8::button_arm
1781 _ctrls.button (FP8Controls::BtnArm).set_active (false);
1782 ARMButtonChange (false);
1786 notify_route_state_changed ();
1790 FaderPort8::notify_stripable_added_or_removed ()
1794 * - session->RouteAdded
1795 * - PresentationInfo::Change
1796 * - Properties::hidden
1797 * - Properties::order
1802 /* called from static PresentationInfo::Change */
1804 FaderPort8::notify_pi_property_changed (const PropertyChange& what_changed)
1806 if (what_changed.contains (Properties::hidden)) {
1807 notify_stripable_added_or_removed ();
1809 if (what_changed.contains (Properties::order)) {
1810 notify_stripable_added_or_removed ();
1812 // Properties::selected is handled via StripableSelectionChanged
1816 FaderPort8::notify_stripable_property_changed (boost::weak_ptr<Stripable> ws, const PropertyChange& what_changed)
1818 boost::shared_ptr<Stripable> s = ws.lock();
1820 assert (0); // this should not happen
1823 if (_assigned_strips.find (s) == _assigned_strips.end()) {
1824 /* it can happen that signal emission is delayed.
1825 * A signal may already be in the queue but the
1826 * _assigned_strips has meanwhile changed.
1828 * before _assigned_strips changes, the connections are dropped
1829 * but that does not seem to invalidate pending requests :(
1831 * Seen when creating a new MB session and Mixbusses are added
1836 uint8_t id = _assigned_strips[s];
1838 if (what_changed.contains (Properties::color)) {
1839 _ctrls.strip(id).set_select_button_color (s->presentation_info ().color());
1842 if (what_changed.empty ()) {
1843 _ctrls.strip(id).set_stripable (s, _ctrls.fader_mode() == ModePan);
1846 if (what_changed.contains (Properties::name)) {
1847 switch (_ctrls.fader_mode ()) {
1849 _ctrls.strip(id).set_text_line (3, s->name(), true);
1853 _ctrls.strip(id).set_text_line (0, s->name());
1864 FaderPort8::stripable_selection_changed ()
1866 if (!_device_active || _chan_locked) {
1869 route_state_connections.drop_connections ();
1870 assign_stripables (false);
1871 subscribe_to_strip_signals ();
1877 FaderPort8::stripable_selection_changed ()
1879 if (!_device_active) {
1880 /* this can be called anytime from the static
1881 * ControlProtocol::StripableSelectionChanged
1885 route_state_connections.drop_connections();
1887 switch (_ctrls.fader_mode ()) {
1889 if (_proc_params.size () > 0 && _showing_well_known < 0) {
1890 /* w/well-known -> re-assign to new strip */
1891 int wk = _showing_well_known;
1892 drop_ctrl_connections ();
1894 } else if (_proc_params.size() == 0) {
1895 /* selecting plugin, update available */
1908 /* update selection lights */
1909 for (StripAssignmentMap::const_iterator i = _assigned_strips.begin(); i != _assigned_strips.end(); ++i) {
1910 boost::shared_ptr<ARDOUR::Stripable> s = i->first;
1911 uint8_t id = i->second;
1912 bool sel = s->is_selected ();
1913 _ctrls.strip(id).select_button ().set_active (sel);
1914 _ctrls.strip(id).select_button ().set_blinking (sel && s == first_selected_stripable ());
1917 subscribe_to_strip_signals ();
1922 FaderPort8::subscribe_to_strip_signals ()
1924 /* keep track of automation-mode of primary selection, shared buttons */
1925 boost::shared_ptr<Stripable> s = first_selected_stripable();
1927 boost::shared_ptr<AutomationControl> ac;
1928 ac = s->gain_control();
1929 if (ac && ac->alist()) {
1930 ac->alist()->automation_state_changed.connect (route_state_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_route_state_changed, this), this);
1932 ac = s->pan_azimuth_control();
1933 if (ac && ac->alist()) {
1934 ac->alist()->automation_state_changed.connect (route_state_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_route_state_changed, this), this);
1937 ac = s->rec_enable_control();
1939 ac->Changed.connect (route_state_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_route_state_changed, this), this);
1944 notify_route_state_changed ();
1948 /* ****************************************************************************
1953 FaderPort8::move_selected_into_view ()
1955 boost::shared_ptr<Stripable> selected = first_selected_stripable ();
1960 StripableList strips;
1961 filter_stripables (strips);
1963 StripableList::iterator it = std::find (strips.begin(), strips.end(), selected);
1964 if (it == strips.end()) {
1967 int off = std::distance (strips.begin(), it);
1969 int channel_off = get_channel_off (_ctrls.mix_mode ());
1970 if (channel_off <= off && off < channel_off + N_STRIPS) {
1974 if (channel_off > off) {
1977 channel_off = off - (N_STRIPS - 1);
1979 set_channel_off (_ctrls.mix_mode (), channel_off);
1984 FaderPort8::select_prev_next (bool next)
1986 StripableList strips;
1987 filter_stripables (strips);
1989 boost::shared_ptr<Stripable> selected = first_selected_stripable ();
1991 if (strips.size() > 0) {
1993 SetStripableSelection (strips.front ());
1995 SetStripableSelection (strips.back ());
2002 boost::shared_ptr<Stripable> toselect;
2003 for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
2004 if (*s == selected) {
2010 if (s != strips.end()) {
2021 if (found && toselect) {
2022 SetStripableSelection (toselect);
2027 FaderPort8::bank (bool down, bool page)
2030 // XXX this should preferably be in actions.cc
2031 AccessAction ("Editor", down ? "select-prev-stripable" : "select-next-stripable");
2035 int dt = page ? N_STRIPS : 1;
2039 set_channel_off (_ctrls.mix_mode (), get_channel_off (_ctrls.mix_mode ()) + dt);
2044 FaderPort8::bank_param (bool down, bool page)
2046 int dt = page ? N_STRIPS : 1;
2050 switch (_ctrls.fader_mode ()) {
2052 if (_proc_params.size() > 0) {
2053 _parameter_off += dt;
2054 assign_processor_ctrls ();