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_insert.h"
41 #include "ardour/processor.h"
42 #include "ardour/rc_configuration.h"
43 #include "ardour/route.h"
44 #include "ardour/session.h"
45 #include "ardour/session_configuration.h"
46 #include "ardour/vca.h"
48 #include "faderport8.h"
50 using namespace ARDOUR;
51 using namespace ArdourSurface;
55 using namespace ArdourSurface::FP8Types;
59 #include "pbd/abstract_ui.cc" // instantiate template
62 //#define VERBOSE_DEBUG
66 debug_2byte_msg (std::string const& msg, int b0, int b1)
69 if (DEBUG_ENABLED(DEBUG::FaderPort8)) {
71 DEBUG_STR_APPEND(a, "RECV: ");
72 DEBUG_STR_APPEND(a, msg);
73 DEBUG_STR_APPEND(a,' ');
74 DEBUG_STR_APPEND(a,hex);
75 DEBUG_STR_APPEND(a,"0x");
76 DEBUG_STR_APPEND(a, b0);
77 DEBUG_STR_APPEND(a,' ');
78 DEBUG_STR_APPEND(a,"0x");
79 DEBUG_STR_APPEND(a, b1);
80 DEBUG_STR_APPEND(a,'\n');
81 DEBUG_TRACE (DEBUG::FaderPort8, DEBUG_STR(a).str());
86 FaderPort8::FaderPort8 (Session& s)
87 : ControlProtocol (s, _("PreSonus FaderPort8"))
88 , AbstractUI<FaderPort8Request> (name())
89 , _connection_state (ConnectionState (0))
90 , _device_active (false)
95 , _blink_onoff (false)
100 boost::shared_ptr<ARDOUR::Port> inp;
101 boost::shared_ptr<ARDOUR::Port> outp;
103 inp = AudioEngine::instance()->register_input_port (DataType::MIDI, "FaderPort8 Recv", true);
104 outp = AudioEngine::instance()->register_output_port (DataType::MIDI, "FaderPort8 Send", true);
105 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(inp);
106 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(outp);
108 if (_input_port == 0 || _output_port == 0) {
109 throw failed_constructor();
112 _input_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Receive)"), true));
113 _output_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Send) "), false));
115 _input_bundle->add_channel (
117 ARDOUR::DataType::MIDI,
118 session->engine().make_port_name_non_relative (inp->name())
121 _output_bundle->add_channel (
123 ARDOUR::DataType::MIDI,
124 session->engine().make_port_name_non_relative (outp->name())
127 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::connection_handler, this, _1, _2, _3, _4, _5), this);
129 StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::gui_track_selection_changed, this), this);
132 _ctrls.FaderModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::notify_fader_mode_changed, this));
133 _ctrls.MixModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::assign_strips, this, true));
136 FaderPort8::~FaderPort8 ()
143 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
144 AudioEngine::instance()->unregister_port (_input_port);
145 _input_port.reset ();
149 _output_port->drain (10000, 250000); /* check every 10 msecs, wait up to 1/4 second for the port to drain */
150 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering output port %1\n", boost::shared_ptr<ARDOUR::Port>(_output_port)->name()));
151 AudioEngine::instance()->unregister_port (_output_port);
152 _output_port.reset ();
157 /* stop event loop */
158 DEBUG_TRACE (DEBUG::FaderPort8, "BaseUI::quit ()\n");
162 /* ****************************************************************************
167 FaderPort8::request_factory (uint32_t num_requests)
169 /* AbstractUI<T>::request_buffer_factory() is a template method only
170 * instantiated in this source module. To provide something visible for
171 * use in the interface/descriptor, we have this static method that is
174 return request_buffer_factory (num_requests);
178 FaderPort8::do_request (FaderPort8Request* req)
180 if (req->type == CallSlot) {
181 call_slot (MISSING_INVALIDATOR, req->the_slot);
182 } else if (req->type == Quit) {
195 FaderPort8::thread_init ()
197 struct sched_param rtparam;
199 pthread_set_name (event_loop_name().c_str());
201 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
202 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
204 memset (&rtparam, 0, sizeof (rtparam));
205 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
207 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
208 // do we care? not particularly.
213 FaderPort8::periodic ()
215 /* prepare TC display -- handled by stripable Periodic () */
216 if (_ctrls.display_timecode ()) {
217 // TODO allow BBT, HHMMSS
218 // used in FP8Strip::periodic_update_timecode
220 session->timecode_time (TC);
221 _timecode = Timecode::timecode_format_time(TC);
226 /* update stripables */
232 FaderPort8::blink_it ()
234 _blink_onoff = !_blink_onoff;
235 BlinkIt (_blink_onoff);
239 /* ****************************************************************************
240 * Port and Signal Connection Management
243 FaderPort8::set_active (bool yn)
245 DEBUG_TRACE (DEBUG::FaderPort8, string_compose("set_active init with yn: '%1'\n", yn));
247 if (yn == active()) {
252 /* start event loop */
254 connect_session_signals ();
260 ControlProtocol::set_active (yn);
261 DEBUG_TRACE (DEBUG::FaderPort8, string_compose("set_active done with yn: '%1'\n", yn));
268 stop_midi_handling ();
269 session_connections.drop_connections ();
270 automation_state_connections.drop_connections ();
271 assigned_stripable_connections.drop_connections ();
272 _assigned_strips.clear ();
273 drop_ctrl_connections ();
274 port_connection.disconnect ();
275 selection_connection.disconnect ();
279 FaderPort8::stop_midi_handling ()
281 _periodic_connection.disconnect ();
282 _blink_connection.disconnect ();
283 midi_connections.drop_connections ();
284 /* Note: the input handler is still active at this point, but we're no
285 * longer connected to any of the parser signals
290 FaderPort8::connected ()
292 DEBUG_TRACE (DEBUG::FaderPort8, "initializing\n");
293 // ideally check firmware version >= 1.01 (USB bcdDevice 0x0101) (vendor 0x194f prod 0x0202)
294 // but we don't have a handle to the underlying USB device here.
296 _channel_off = _plugin_off = _parameter_off = 0;
297 _blink_onoff = false;
301 start_midi_handling ();
302 _ctrls.initialize ();
304 /* highlight bound user-actions */
305 for (FP8Controls::UserButtonMap::const_iterator i = _ctrls.user_buttons ().begin ();
306 i != _ctrls.user_buttons ().end (); ++i) {
307 _ctrls.button (i->first).set_active (! _user_action_map[i->first].empty ());
309 /* shift button lights */
310 tx_midi3 (0x90, 0x06, 0x00);
311 tx_midi3 (0x90, 0x46, 0x00);
313 send_session_state ();
314 assign_strips (true);
316 Glib::RefPtr<Glib::TimeoutSource> blink_timer =
317 Glib::TimeoutSource::create (200);
318 _blink_connection = blink_timer->connect (sigc::mem_fun (*this, &FaderPort8::blink_it));
319 blink_timer->attach (main_loop()->get_context());
321 Glib::RefPtr<Glib::TimeoutSource> periodic_timer =
322 Glib::TimeoutSource::create (100);
323 _periodic_connection = periodic_timer->connect (sigc::mem_fun (*this, &FaderPort8::periodic));
324 periodic_timer->attach (main_loop()->get_context());
328 FaderPort8::disconnected ()
330 stop_midi_handling ();
331 if (_device_active) {
332 for (uint8_t id = 0; id < 8; ++id) {
333 _ctrls.strip(id).unset_controllables ();
335 _ctrls.all_lights_off ();
340 FaderPort8::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
343 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::connection_handler: start\n");
345 if (!_input_port || !_output_port) {
349 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
350 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
352 if (ni == name1 || ni == name2) {
354 _connection_state |= InputConnected;
356 _connection_state &= ~InputConnected;
358 } else if (no == name1 || no == name2) {
360 _connection_state |= OutputConnected;
362 _connection_state &= ~OutputConnected;
366 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
372 if ((_connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
374 /* XXX this is a horrible hack. Without a short sleep here,
375 * something prevents the device wakeup messages from being
376 * sent and/or the responses from being received.
379 DEBUG_TRACE (DEBUG::FaderPort8, "device now connected for both input and output\n");
381 _device_active = true;
384 DEBUG_TRACE (DEBUG::FaderPort8, "Device disconnected (input or output or both) or not yet fully connected\n");
386 _device_active = false;
389 ConnectionChange (); /* emit signal for our GUI */
392 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::connection_handler: end\n");
395 return true; /* connection status changed */
398 list<boost::shared_ptr<ARDOUR::Bundle> >
399 FaderPort8::bundles ()
401 list<boost::shared_ptr<ARDOUR::Bundle> > b;
404 b.push_back (_input_bundle);
405 b.push_back (_output_bundle);
411 /* ****************************************************************************
415 FaderPort8::midi_input_handler (Glib::IOCondition ioc, boost::weak_ptr<ARDOUR::AsyncMIDIPort> wport)
417 boost::shared_ptr<AsyncMIDIPort> port (wport.lock());
424 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("something happend on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
435 DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("data available on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
437 framepos_t now = session->engine().sample_time();
445 FaderPort8::start_midi_handling ()
447 _input_port->parser()->sysex.connect_same_thread (midi_connections, boost::bind (&FaderPort8::sysex_handler, this, _1, _2, _3));
448 _input_port->parser()->poly_pressure.connect_same_thread (midi_connections, boost::bind (&FaderPort8::polypressure_handler, this, _1, _2));
449 for (uint8_t i = 0; i < 16; ++i) {
450 _input_port->parser()->channel_pitchbend[i].connect_same_thread (midi_connections, boost::bind (&FaderPort8::pitchbend_handler, this, _1, i, _2));
452 _input_port->parser()->controller.connect_same_thread (midi_connections, boost::bind (&FaderPort8::controller_handler, this, _1, _2));
453 _input_port->parser()->note_on.connect_same_thread (midi_connections, boost::bind (&FaderPort8::note_on_handler, this, _1, _2));
454 _input_port->parser()->note_off.connect_same_thread (midi_connections, boost::bind (&FaderPort8::note_off_handler, this, _1, _2));
456 /* This connection means that whenever data is ready from the input
457 * port, the relevant thread will invoke our ::midi_input_handler()
458 * method, which will read the data, and invoke the parser.
460 _input_port->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &FaderPort8::midi_input_handler), boost::weak_ptr<AsyncMIDIPort> (_input_port)));
461 _input_port->xthread().attach (main_loop()->get_context());
465 FaderPort8::tx_midi (std::vector<uint8_t> const& d) const
467 /* work around midi buffer overflow for batch changes */
468 if (d.size() == 3 && (d[0] == 0x91 || d[0] == 0x92)) {
469 /* set colors triplet in one go */
470 } else if (d.size() == 3 && (d[0] == 0x93)) {
473 g_usleep (400 * d.size());
476 size_t tx = _output_port->write (&d[0], d.size(), 0);
477 assert (tx == d.size());
480 return _output_port->write (&d[0], d.size(), 0);
484 /* ****************************************************************************
488 FaderPort8::polypressure_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
490 debug_2byte_msg ("PP", tb->controller_number, tb->value);
491 // outgoing only (meter)
495 FaderPort8::pitchbend_handler (MIDI::Parser &, uint8_t chan, MIDI::pitchbend_t pb)
497 debug_2byte_msg ("PB", chan, pb);
498 /* fader 0..16368 (0x3ff0 -- 1024 steps) */
499 bool handled = _ctrls.midi_fader (chan, pb);
500 /* if Shift key is held while moving a fader (group override), don't lock shift. */
501 if ((_shift_pressed > 0) && handled) {
502 _shift_connection.disconnect ();
508 FaderPort8::controller_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
510 debug_2byte_msg ("CC", tb->controller_number, tb->value);
512 // val Bit 7 = direction, Bits 0-6 = number of steps
513 if (tb->controller_number == 0x3c) {
514 encoder_navigate (tb->value & 0x40 ? true : false, tb->value & 0x3f);
516 if (tb->controller_number == 0x10) {
517 encoder_parameter (tb->value & 0x40 ? true : false, tb->value & 0x3f);
522 FaderPort8::note_on_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
524 debug_2byte_msg ("ON", tb->note_number, tb->velocity);
527 if (tb->note_number >= 0x68 && tb->note_number <= 0x6f) {
528 _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
532 /* special case shift */
533 if (tb->note_number == 0x06 || tb->note_number == 0x46) {
534 _shift_pressed |= (tb->note_number == 0x06) ? 1 : 2;
535 if (_shift_pressed == 3) {
538 _shift_connection.disconnect ();
541 ShiftButtonChange (false);
542 tx_midi3 (0x90, 0x06, 0x00);
543 tx_midi3 (0x90, 0x46, 0x00);
547 Glib::RefPtr<Glib::TimeoutSource> shift_timer =
548 Glib::TimeoutSource::create (1000);
549 shift_timer->attach (main_loop()->get_context());
550 _shift_connection = shift_timer->connect (sigc::mem_fun (*this, &FaderPort8::shift_timeout));
552 ShiftButtonChange (true);
553 tx_midi3 (0x90, 0x06, 0x7f);
554 tx_midi3 (0x90, 0x46, 0x7f);
558 _ctrls.midi_event (tb->note_number, tb->velocity);
562 FaderPort8::note_off_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
564 debug_2byte_msg ("OF", tb->note_number, tb->velocity);
566 if (tb->note_number >= 0x68 && tb->note_number <= 0x6f) {
568 _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
572 /* special case shift */
573 if (tb->note_number == 0x06 || tb->note_number == 0x46) {
574 _shift_pressed &= (tb->note_number == 0x06) ? 2 : 1;
575 if (_shift_pressed > 0) {
581 ShiftButtonChange (false);
582 tx_midi3 (0x90, 0x06, 0x00);
583 tx_midi3 (0x90, 0x46, 0x00);
584 /* just in case this happens concurrently */
585 _shift_connection.disconnect ();
590 bool handled = _ctrls.midi_event (tb->note_number, tb->velocity);
591 /* if Shift key is held while activating an action, don't lock shift. */
592 if ((_shift_pressed > 0) && handled) {
593 _shift_connection.disconnect ();
599 FaderPort8::sysex_handler (MIDI::Parser &p, MIDI::byte *buf, size_t size)
602 if (DEBUG_ENABLED(DEBUG::FaderPort8)) {
604 DEBUG_STR_APPEND(a, string_compose ("RECV sysex siz=%1", size));
605 for (size_t i=0; i < size; ++i) {
606 DEBUG_STR_APPEND(a,hex);
607 DEBUG_STR_APPEND(a,"0x");
608 DEBUG_STR_APPEND(a,(int)buf[i]);
609 DEBUG_STR_APPEND(a,' ');
611 DEBUG_STR_APPEND(a,'\n');
612 DEBUG_TRACE (DEBUG::FaderPort8, DEBUG_STR(a).str());
617 /* ****************************************************************************
621 FaderPort8::set_button_action (FP8Controls::ButtonId id, bool press, std::string const& action_name)
623 if (_ctrls.user_buttons().find (id) == _ctrls.user_buttons().end ()) {
626 _user_action_map[id].action (press).assign_action (action_name);
628 if (!_device_active) {
631 _ctrls.button (id).set_active (!_user_action_map[id].empty ());
635 FaderPort8::get_button_action (FP8Controls::ButtonId id, bool press)
637 return _user_action_map[id].action(press)._action_name;
640 /* ****************************************************************************
644 FaderPort8::get_state ()
646 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::get_state\n");
647 XMLNode& node (ControlProtocol::get_state());
651 child = new XMLNode (X_("Input"));
652 child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_input_port)->get_state());
653 node.add_child_nocopy (*child);
655 child = new XMLNode (X_("Output"));
656 child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_output_port)->get_state());
657 node.add_child_nocopy (*child);
659 for (UserActionMap::const_iterator i = _user_action_map.begin (); i != _user_action_map.end (); ++i) {
660 if (i->second.empty()) {
664 if (!_ctrls.button_enum_to_name (i->first, name)) {
667 XMLNode* btn = new XMLNode (X_("Button"));
668 btn->add_property (X_("id"), name);
669 if (!i->second.action(true).empty ()) {
670 btn->add_property ("press", i->second.action(true)._action_name);
672 if (!i->second.action(false).empty ()) {
673 btn->add_property ("release", i->second.action(false)._action_name);
675 node.add_child_nocopy (*btn);
682 FaderPort8::set_state (const XMLNode& node, int version)
684 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state\n");
686 XMLNodeConstIterator niter;
687 XMLNode const* child;
689 if (ControlProtocol::set_state (node, version)) {
693 if ((child = node.child (X_("Input"))) != 0) {
694 XMLNode* portnode = child->child (Port::state_node_name.c_str());
696 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state Input\n");
697 boost::shared_ptr<ARDOUR::Port>(_input_port)->set_state (*portnode, version);
701 if ((child = node.child (X_("Output"))) != 0) {
702 XMLNode* portnode = child->child (Port::state_node_name.c_str());
704 DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::set_state Output\n");
705 boost::shared_ptr<ARDOUR::Port>(_output_port)->set_state (*portnode, version);
709 _user_action_map.clear ();
710 // TODO: When re-loading state w/o surface re-init becomes possible,
711 // unset lights and reset colors of user buttons.
713 for (XMLNodeList::const_iterator n = node.children().begin(); n != node.children().end(); ++n) {
714 if ((*n)->name() != X_("Button")) {
717 XMLProperty const* prop = (*n)->property (X_("id"));
722 FP8Controls::ButtonId id;
723 if (!_ctrls.button_name_to_enum (prop->value(), id)) {
727 prop = (*n)->property (X_("press"));
729 set_button_action (id, true, prop->value());
731 prop = (*n)->property (X_("release"));
733 set_button_action (id, false, prop->value());
740 /* ****************************************************************************
741 * Stripable Assignment
744 static bool flt_audio_track (boost::shared_ptr<Stripable> s) {
745 return boost::dynamic_pointer_cast<AudioTrack>(s) != 0;
748 static bool flt_midi_track (boost::shared_ptr<Stripable> s) {
749 return boost::dynamic_pointer_cast<MidiTrack>(s) != 0;
752 static bool flt_bus (boost::shared_ptr<Stripable> s) {
753 if (boost::dynamic_pointer_cast<Route>(s) == 0) {
757 if (s->mixbus () == 0) {
761 return boost::dynamic_pointer_cast<Track>(s) == 0;
764 static bool flt_auxbus (boost::shared_ptr<Stripable> s) {
765 if (boost::dynamic_pointer_cast<Route>(s) == 0) {
769 if (s->mixbus () > 0) {
773 return boost::dynamic_pointer_cast<Track>(s) == 0;
776 static bool flt_vca (boost::shared_ptr<Stripable> s) {
777 return boost::dynamic_pointer_cast<VCA>(s) != 0;
780 static bool flt_selected (boost::shared_ptr<Stripable> s) {
781 return s->is_selected ();
784 static bool flt_mains (boost::shared_ptr<Stripable> s) {
785 return (s->is_master() || s->is_monitor());
788 static bool flt_all (boost::shared_ptr<Stripable> s) {
792 static bool flt_rec_armed (boost::shared_ptr<Stripable> s) {
793 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
797 return t->rec_enable_control ()->get_value () > 0.;
800 static bool flt_instrument (boost::shared_ptr<Stripable> s) {
801 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(s);
805 return 0 != r->the_instrument ();
808 struct FP8SortByNewDisplayOrder
811 bool operator () (const boost::shared_ptr<Stripable> & a, const boost::shared_ptr<Stripable> & b) const
813 if (a->presentation_info().flags () == b->presentation_info().flags ()) {
814 return a->presentation_info().order() < b->presentation_info().order();
820 if (a->presentation_info().flags () & ARDOUR::PresentationInfo::VCA) {
824 else if (a->presentation_info().flags () & ARDOUR::PresentationInfo::MasterOut) {
827 else if (a->presentation_info().flags () & ARDOUR::PresentationInfo::Mixbus || a->mixbus()) {
832 if (b->presentation_info().flags () & ARDOUR::PresentationInfo::VCA) {
836 else if (b->presentation_info().flags () & ARDOUR::PresentationInfo::MasterOut) {
839 else if (b->presentation_info().flags () & ARDOUR::PresentationInfo::Mixbus || b->mixbus()) {
845 // this can happen with older MB sessions (no PresentationInfo::Mixbus flag)
846 if (cmp_a == cmp_b) {
847 return a->presentation_info().order() < b->presentation_info().order();
850 return cmp_a < cmp_b;
855 FaderPort8::filter_stripables (StripableList& strips) const
857 typedef bool (*FilterFunction)(boost::shared_ptr<Stripable>);
860 bool allow_master = false;
861 bool allow_monitor = false;
863 switch (_ctrls.mix_mode ()) {
865 flt = &flt_audio_track;
868 flt = &flt_instrument;
877 flt = &flt_midi_track;
885 allow_monitor = true;
889 flt = &flt_rec_armed;
901 session->get_stripables (all);
903 for (StripableList::const_iterator s = all.begin(); s != all.end(); ++s) {
904 if ((*s)->is_auditioner ()) { continue; }
905 if ((*s)->is_hidden ()) { continue; }
907 if (!allow_master && (*s)->is_master ()) { continue; }
908 if (!allow_monitor && (*s)->is_monitor ()) { continue; }
911 strips.push_back (*s);
914 strips.sort (FP8SortByNewDisplayOrder());
917 /* Track/Pan mode: assign stripable to strips */
919 FaderPort8::assign_stripables (bool select_only)
921 StripableList strips;
922 filter_stripables (strips);
925 set_periodic_display_mode (FP8Strip::Stripables);
928 int n_strips = strips.size();
929 _channel_off = std::min (_channel_off, n_strips - 8);
930 _channel_off = std::max (0, _channel_off);
933 int skip = _channel_off;
934 for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
940 _assigned_strips[*s] = id;
941 (*s)->DropReferences.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
942 boost::bind (&FaderPort8::notify_stripable_added_or_removed, this), this);
944 (*s)->PropertyChanged.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
945 boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
946 (*s)->presentation_info ().PropertyChanged.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
947 boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
950 _ctrls.strip(id).set_text_line (3, (*s)->name (), true);
951 _ctrls.strip(id).select_button ().set_color ((*s)->presentation_info ().color());
952 /* update selection lights */
953 _ctrls.strip(id).select_button ().set_active ((*s)->is_selected ());
954 _ctrls.strip(id).select_button ().set_blinking (*s == first_selected_stripable ());
956 _ctrls.strip(id).set_stripable (*s, _ctrls.fader_mode() == ModePan);
959 boost::function<void ()> cb (boost::bind (&FaderPort8::select_strip, this, boost::weak_ptr<Stripable> (*s)));
960 _ctrls.strip(id).set_select_cb (cb);
966 for (; id < 8; ++id) {
967 _ctrls.strip(id).unset_controllables (select_only ? (FP8Strip::CTRL_SELECT | FP8Strip::CTRL_TEXT3) : FP8Strip::CTRL_ALL);
972 FaderPort8::assign_processor_ctrls ()
974 if (_proc_params.size() == 0) {
975 _ctrls.set_fader_mode (ModeTrack);
978 set_periodic_display_mode (FP8Strip::PluginParam);
980 std::vector <ProcessorCtrl*> toggle_params;
981 std::vector <ProcessorCtrl*> slider_params;
983 for ( std::list <ProcessorCtrl>::iterator i = _proc_params.begin(); i != _proc_params.end(); ++i) {
984 if ((*i).ac->toggled()) {
985 toggle_params.push_back (&(*i));
987 slider_params.push_back (&(*i));
991 int n_parameters = std::max (toggle_params.size(), slider_params.size());
993 _parameter_off = std::min (_parameter_off, n_parameters - 8);
994 _parameter_off = std::max (0, _parameter_off);
997 for (size_t i = _parameter_off; i < (size_t)n_parameters; ++i) {
998 if (i >= toggle_params.size ()) {
999 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT1);
1001 else if (i >= slider_params.size ()) {
1002 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_SELECT & ~FP8Strip::CTRL_TEXT3);
1004 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT1 & ~FP8Strip::CTRL_SELECT & ~FP8Strip::CTRL_TEXT3);
1007 if (i < slider_params.size ()) {
1008 _ctrls.strip(id).set_fader_controllable (slider_params[i]->ac);
1009 _ctrls.strip(id).set_text_line (0, slider_params[i]->name);
1011 if (i < toggle_params.size ()) {
1012 _ctrls.strip(id).set_select_controllable (toggle_params[i]->ac);
1013 _ctrls.strip(id).set_text_line (3, toggle_params[i]->name, true);
1021 for (; id < 8; ++id) {
1022 _ctrls.strip(id).unset_controllables ();
1027 FaderPort8::build_well_known_processor_ctrls (boost::shared_ptr<Stripable> s, bool eq)
1029 #define PUSH_BACK_NON_NULL(N, C) do {if (C) { _proc_params.push_back (ProcessorCtrl (N, C)); }} while (0)
1031 _proc_params.clear ();
1033 int cnt = s->eq_band_cnt();
1034 PUSH_BACK_NON_NULL ("Enable", s->eq_enable_controllable ());
1035 PUSH_BACK_NON_NULL ("HPF", s->eq_hpf_controllable ());
1036 for (int band = 0; band < cnt; ++band) {
1037 std::string bn = s->eq_band_name (band);
1038 PUSH_BACK_NON_NULL (string_compose ("Gain %1", bn), s->eq_gain_controllable (band));
1039 PUSH_BACK_NON_NULL (string_compose ("Freq %1", bn), s->eq_freq_controllable (band));
1040 PUSH_BACK_NON_NULL (string_compose ("Band %1", bn), s->eq_q_controllable (band));
1041 PUSH_BACK_NON_NULL (string_compose ("Shape %1", bn), s->eq_shape_controllable (band));
1044 PUSH_BACK_NON_NULL ("Enable", s->comp_enable_controllable ());
1045 PUSH_BACK_NON_NULL ("Threshold", s->comp_threshold_controllable ());
1046 PUSH_BACK_NON_NULL ("Speed", s->comp_speed_controllable ());
1047 PUSH_BACK_NON_NULL ("Mode", s->comp_mode_controllable ());
1052 FaderPort8::select_plugin (int num)
1054 // make sure drop_ctrl_connections() was called
1055 assert (_proc_params.size() == 0 && _showing_well_known == 0);
1057 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (first_selected_stripable());
1059 _ctrls.set_fader_mode (ModeTrack);
1063 build_well_known_processor_ctrls (r, num == -1);
1064 assign_processor_ctrls ();
1065 _showing_well_known = num;
1068 _showing_well_known = 0;
1070 boost::shared_ptr<Processor> proc = r->nth_plugin (num);
1072 _ctrls.set_fader_mode (ModeTrack);
1076 // switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed()
1077 // which drops the references, disconnects the signal and re-spills tracks
1078 proc->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1081 _proc_params.clear();
1082 set<Evoral::Parameter> p = proc->what_can_be_automated ();
1083 for (set<Evoral::Parameter>::iterator i = p.begin(); i != p.end(); ++i) {
1084 std::string n = proc->describe_parameter (*i);
1085 if (n == "hidden") {
1088 _proc_params.push_back (ProcessorCtrl (n, proc->automation_control (*i)));
1091 // TODO: open plugin GUI if (_proc_params.size() > 0)
1094 assign_processor_ctrls ();
1097 /* short 4 chars at most */
1098 static std::string plugintype (ARDOUR::PluginType t) {
1115 return enum_2_string (t);
1119 FaderPort8::spill_plugins ()
1121 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (first_selected_stripable());
1123 _ctrls.set_fader_mode (ModeTrack);
1127 drop_ctrl_connections ();
1129 // switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed()
1130 // which drops the references, disconnects the signal and re-spills tracks
1131 r->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1133 // update when processor change
1134 r->processors_changed.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::spill_plugins, this), this);
1137 boost::shared_ptr<Processor> proc;
1139 std::vector<uint32_t> procs;
1141 for (uint32_t i = 0; 0 != (proc = r->nth_plugin (i)); ++i) {
1142 if (!proc->display_to_user ()) {
1144 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1145 if (pi->is_channelstrip ()) // don't skip MB PRE
1150 set<Evoral::Parameter> p = proc->what_can_be_automated ();
1151 for (set<Evoral::Parameter>::iterator i = p.begin(); i != p.end(); ++i) {
1152 std::string n = proc->describe_parameter (*i);
1153 if (n == "hidden") {
1158 if (n_controls > 0) {
1159 procs.push_back (i);
1163 int n_plugins = procs.size();
1165 bool have_well_known_eq = false;
1166 bool have_well_known_comp = false;
1168 // reserve last slot(s) for "well-known"
1169 if (r->eq_band_cnt() > 0) {
1171 have_well_known_eq = true;
1173 if (r->comp_enable_controllable ()) {
1175 have_well_known_comp = true;
1178 if (n_plugins == 0 && !have_well_known_eq && !have_well_known_comp) {
1179 _ctrls.set_fader_mode (ModeTrack);
1183 set_periodic_display_mode (FP8Strip::PluginSelect);
1185 _plugin_off = std::min (_plugin_off, n_plugins - spillwidth);
1186 _plugin_off = std::max (0, _plugin_off);
1189 for (uint32_t i = _plugin_off; ; ++i) {
1190 if (i >= procs.size()) {
1193 boost::shared_ptr<Processor> proc = r->nth_plugin (procs[i]);
1197 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
1198 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, procs[i]));
1200 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1201 _ctrls.strip(id).set_select_cb (cb);
1202 _ctrls.strip(id).select_button ().set_color (0x00ff00ff);
1203 _ctrls.strip(id).select_button ().set_active (true /*proc->enabled()*/);
1204 _ctrls.strip(id).select_button ().set_blinking (false);
1205 _ctrls.strip(id).set_text_line (0, proc->name());
1206 _ctrls.strip(id).set_text_line (1, pi->plugin()->maker());
1207 _ctrls.strip(id).set_text_line (2, plugintype (pi->type()));
1208 _ctrls.strip(id).set_text_line (3, "");
1210 if (++id == spillwidth) {
1215 for (; id < spillwidth; ++id) {
1216 _ctrls.strip(id).unset_controllables ();
1219 if (have_well_known_comp) {
1221 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -2));
1222 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1223 _ctrls.strip(id).set_select_cb (cb);
1224 _ctrls.strip(id).select_button ().set_color (0xffff00ff);
1225 _ctrls.strip(id).select_button ().set_active (true);
1226 _ctrls.strip(id).select_button ().set_blinking (false);
1227 _ctrls.strip(id).set_text_line (0, "Comp");
1228 _ctrls.strip(id).set_text_line (1, "Built-In");
1229 _ctrls.strip(id).set_text_line (2, "--");
1230 _ctrls.strip(id).set_text_line (3, "");
1233 if (have_well_known_eq) {
1235 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -1));
1236 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
1237 _ctrls.strip(id).set_select_cb (cb);
1238 _ctrls.strip(id).select_button ().set_color (0xffff00ff);
1239 _ctrls.strip(id).select_button ().set_active (true);
1240 _ctrls.strip(id).select_button ().set_blinking (false);
1241 _ctrls.strip(id).set_text_line (0, "EQ");
1242 _ctrls.strip(id).set_text_line (1, "Built-In");
1243 _ctrls.strip(id).set_text_line (2, "--");
1244 _ctrls.strip(id).set_text_line (3, "");
1251 FaderPort8::assign_sends ()
1253 boost::shared_ptr<Stripable> s = first_selected_stripable();
1255 _ctrls.set_fader_mode (ModeTrack);
1260 while (0 != s->send_level_controllable (n_sends)) {
1264 _ctrls.set_fader_mode (ModeTrack);
1268 drop_ctrl_connections ();
1269 s->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
1271 set_periodic_display_mode (FP8Strip::SendDisplay);
1273 _plugin_off = std::min (_plugin_off, n_sends - 8);
1274 _plugin_off = std::max (0, _plugin_off);
1277 int skip = _parameter_off;
1278 for (uint32_t i = _plugin_off; ; ++i) {
1283 boost::shared_ptr<AutomationControl> send = s->send_level_controllable (i);
1288 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT1 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1289 _ctrls.strip(id).set_fader_controllable (send);
1290 _ctrls.strip(id).set_text_line (0, s->send_name (i));
1291 _ctrls.strip(id).set_mute_controllable (s->send_enable_controllable (i));
1298 for (; id < 8; ++id) {
1299 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
1301 #ifdef MIXBUS // master-assign on last solo
1302 _ctrls.strip(7).set_solo_controllable (s->master_send_enable_controllable ());
1304 /* set select buttons */
1305 assigned_stripable_connections.drop_connections ();
1306 _assigned_strips.clear ();
1307 assign_stripables (true);
1311 FaderPort8::set_periodic_display_mode (FP8Strip::DisplayMode m)
1313 for (uint8_t id = 0; id < 8; ++id) {
1314 _ctrls.strip(id).set_periodic_display_mode (m);
1319 FaderPort8::assign_strips (bool reset_bank)
1325 assigned_stripable_connections.drop_connections ();
1326 _assigned_strips.clear ();
1328 FaderMode fadermode = _ctrls.fader_mode ();
1329 switch (fadermode) {
1332 assign_stripables ();
1333 gui_track_selection_changed (); // update selection, automation-state
1336 if (_proc_params.size() > 0) {
1337 assign_processor_ctrls ();
1350 FaderPort8::drop_ctrl_connections ()
1352 _proc_params.clear();
1353 processor_connections.drop_connections ();
1354 _showing_well_known = 0;
1358 FaderPort8::notify_fader_mode_changed ()
1360 FaderMode fadermode = _ctrls.fader_mode ();
1362 boost::shared_ptr<Stripable> s = first_selected_stripable();
1363 if (!s && (fadermode == ModePlugins || fadermode == ModeSend)) {
1364 _ctrls.set_fader_mode (ModeTrack);
1368 drop_ctrl_connections ();
1370 switch (fadermode) {
1378 // force unset rec-arm button, see also FaderPort8::button_arm
1379 _ctrls.button (FP8Controls::BtnArm).set_active (false);
1380 ARMButtonChange (false);
1383 assign_strips (false);
1384 notify_automation_mode_changed ();
1387 /* ****************************************************************************
1388 * Assigned Stripable Callbacks
1392 FaderPort8::notify_stripable_added_or_removed ()
1396 * - session->RouteAdded
1397 * - PresentationInfo::Change
1398 * - Properties::hidden
1399 * - Properties::order
1401 assign_strips (false);
1404 /* functor for FP8Strip's select button */
1406 FaderPort8::select_strip (boost::weak_ptr<Stripable> ws)
1408 boost::shared_ptr<Stripable> s = ws.lock();
1413 if (s->is_selected ()) {
1414 RemoveStripableFromSelection (s);
1416 SetStripableSelection (s);
1420 if (s->is_selected () && s != first_selected_stripable ()) {
1421 set_first_selected_stripable (s);
1422 gui_track_selection_changed ();
1424 ToggleStripableSelection (s);
1428 /* called from static PresentationInfo::Change */
1430 FaderPort8::notify_pi_property_changed (const PropertyChange& what_changed)
1432 if (what_changed.contains (Properties::hidden)) {
1433 notify_stripable_added_or_removed ();
1435 if (what_changed.contains (Properties::order)) {
1436 notify_stripable_added_or_removed ();
1438 // Properties::selected is handled via StripableSelectionChanged
1442 FaderPort8::notify_stripable_property_changed (boost::weak_ptr<Stripable> ws, const PropertyChange& what_changed)
1444 boost::shared_ptr<Stripable> s = ws.lock();
1446 assert (0); // this should not happen
1449 if (_assigned_strips.find (s) == _assigned_strips.end()) {
1450 /* it can happen that signal emission is delayed.
1451 * A signal may already be in the queue but the
1452 * _assigned_strips has meanwhile changed.
1454 * before _assigned_strips changes, the connections are dropped
1455 * but that does not seem to invalidate pending requests :(
1457 * Seen when creating a new MB session and Mixbusses are added
1462 uint8_t id = _assigned_strips[s];
1464 if (what_changed.contains (Properties::color)) {
1465 _ctrls.strip(id).select_button ().set_color (s->presentation_info ().color());
1468 if (what_changed.contains (Properties::name)) {
1469 switch (_ctrls.fader_mode ()) {
1471 _ctrls.strip(id).set_text_line (3, s->name(), true);
1475 _ctrls.strip(id).set_text_line (0, s->name());
1485 FaderPort8::gui_track_selection_changed (/*ARDOUR::StripableNotificationListPtr*/)
1487 automation_state_connections.drop_connections();
1489 switch (_ctrls.fader_mode ()) {
1491 if (_proc_params.size () > 0 && _showing_well_known < 0) {
1492 /* w/well-known -> re-assign to new strip */
1493 int wk = _showing_well_known;
1494 drop_ctrl_connections ();
1507 /* update selection lights */
1508 for (StripAssignmentMap::const_iterator i = _assigned_strips.begin(); i != _assigned_strips.end(); ++i) {
1509 boost::shared_ptr<ARDOUR::Stripable> s = i->first;
1510 uint8_t id = i->second;
1511 bool sel = s->is_selected ();
1512 _ctrls.strip(id).select_button ().set_active (sel);
1513 _ctrls.strip(id).select_button ().set_blinking (sel && s == first_selected_stripable ());
1516 /* track automation-mode of primary selection */
1517 boost::shared_ptr<Stripable> s = first_selected_stripable();
1519 boost::shared_ptr<AutomationControl> ac;
1520 ac = s->gain_control();
1521 if (ac && ac->alist()) {
1522 ac->alist()->automation_state_changed.connect (automation_state_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_automation_mode_changed, this), this);
1524 ac = s->pan_azimuth_control();
1525 if (ac && ac->alist()) {
1526 ac->alist()->automation_state_changed.connect (automation_state_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_automation_mode_changed, this), this);
1530 notify_automation_mode_changed ();
1534 /* ****************************************************************************
1539 FaderPort8::move_selected_into_view ()
1541 boost::shared_ptr<Stripable> selected = first_selected_stripable ();
1546 StripableList strips;
1547 filter_stripables (strips);
1549 StripableList::iterator it = std::find (strips.begin(), strips.end(), selected);
1550 if (it == strips.end()) {
1553 int off = std::distance (strips.begin(), it);
1555 if (_channel_off <= off && off < _channel_off + 8) {
1559 if (_channel_off > off) {
1562 _channel_off = off - 7;
1564 assign_strips (false);
1568 FaderPort8::select_prev_next (bool next)
1570 StripableList strips;
1571 filter_stripables (strips);
1573 boost::shared_ptr<Stripable> selected = first_selected_stripable ();
1575 if (strips.size() > 0) {
1577 SetStripableSelection (strips.front ());
1579 SetStripableSelection (strips.back ());
1586 boost::shared_ptr<Stripable> toselect;
1587 for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1588 if (*s == selected) {
1594 if (s != strips.end()) {
1605 if (found && toselect) {
1606 SetStripableSelection (toselect);
1611 FaderPort8::bank (bool down, bool page)
1613 int dt = page ? 8 : 1;
1618 assign_strips (false);
1622 FaderPort8::bank_param (bool down, bool page)
1624 int dt = page ? 8 : 1;
1628 switch (_ctrls.fader_mode ()) {
1630 if (_proc_params.size() > 0) {
1631 _parameter_off += dt;
1632 assign_processor_ctrls ();