X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fsurfaces%2Fmackie%2Fmackie_control_protocol.cc;h=9887ed9402a37f7115ae4758f2f3bea6a6fa9595;hb=52021bc3ca1fe176cc32a0fcbc37b375a49d2bfc;hp=45e2a9b0cb5715c6a7bcf1ccf181d083574e539c;hpb=8bb4ac0ac71c84f9ba588df76fa94be3710f7dfe;p=ardour.git diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index 45e2a9b0cb..9887ed9402 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -1,21 +1,26 @@ /* - Copyright (C) 2006,2007 John Anderson - Copyright (C) 2012 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + * Copyright (C) 2006-2007 John Anderson + * Copyright (C) 2007-2010 David Robillard + * Copyright (C) 2007-2017 Paul Davis + * Copyright (C) 2009-2012 Carl Hetherington + * Copyright (C) 2015-2016 Len Ovens + * Copyright (C) 2015-2019 Robin Gareus + * Copyright (C) 2016-2018 Ben Loftis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ #include #include @@ -52,6 +57,7 @@ #include "ardour/panner.h" #include "ardour/panner_shell.h" #include "ardour/profile.h" +#include "ardour/record_enable_control.h" #include "ardour/route.h" #include "ardour/route_group.h" #include "ardour/session.h" @@ -59,6 +65,7 @@ #include "ardour/track.h" #include "ardour/types.h" #include "ardour/audioengine.h" +#include "ardour/vca_manager.h" #include "mackie_control_protocol.h" @@ -81,7 +88,7 @@ using namespace Glib; using namespace ArdourSurface; using namespace Mackie; -#include "i18n.h" +#include "pbd/i18n.h" #include "pbd/abstract_ui.cc" // instantiate template @@ -91,6 +98,8 @@ const int MackieControlProtocol::MODIFIER_SHIFT = 0x4; const int MackieControlProtocol::MODIFIER_CMDALT = 0x8; const int MackieControlProtocol::MODIFIER_ZOOM = 0x10; const int MackieControlProtocol::MODIFIER_SCRUB = 0x20; +const int MackieControlProtocol::MODIFIER_MARKER = 0x40; +const int MackieControlProtocol::MODIFIER_NUDGE = 0x80; const int MackieControlProtocol::MAIN_MODIFIER_MASK = (MackieControlProtocol::MODIFIER_OPTION| MackieControlProtocol::MODIFIER_CONTROL| MackieControlProtocol::MODIFIER_SHIFT| @@ -105,16 +114,15 @@ bool MackieControlProtocol::probe() MackieControlProtocol::MackieControlProtocol (Session& session) : ControlProtocol (session, X_("Mackie")) - , AbstractUI ("mackie") + , AbstractUI (name()) , _current_initial_bank (0) - , _frame_last (0) + , _sample_last (0) , _timecode_type (ARDOUR::AnyTime::BBT) , _gui (0) , _scrub_mode (false) , _flip_mode (Normal) , _view_mode (Mixer) , _subview_mode (None) - , _pot_mode (Pan) , _current_selected_track (-1) , _modifier_state (0) , _ipmidi_base (MIDI::IPMIDIPort::lowest_ipmidi_port_default) @@ -123,6 +131,8 @@ MackieControlProtocol::MackieControlProtocol (Session& session) , _initialized (false) , configuration_state (0) , state_version (0) + , marker_modifier_consumed_by_button (false) + , nudge_modifier_consumed_by_button (false) { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::MackieControlProtocol\n"); @@ -133,9 +143,7 @@ MackieControlProtocol::MackieControlProtocol (Session& session) _last_bank[i] = 0; } - _last_bank[Mixer] = _current_selected_track; - - TrackSelectionChanged.connect (gui_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::gui_track_selection_changed, this, _1, true), this); + PresentationInfo::Change.connect (gui_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_presentation_info_changed, this, _1), this); _instance = this; @@ -181,19 +189,12 @@ MackieControlProtocol::~MackieControlProtocol() void MackieControlProtocol::thread_init () { - struct sched_param rtparam; - - pthread_set_name (X_("MackieControl")); + pthread_set_name (event_loop_name().c_str()); - PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("MackieControl"), 2048); - ARDOUR::SessionEvent::create_per_thread_pool (X_("MackieControl"), 128); + PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048); + ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128); - memset (&rtparam, 0, sizeof (rtparam)); - rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */ - - if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) { - // do we care? not particularly. - } + set_thread_priority (); } void @@ -209,7 +210,6 @@ MackieControlProtocol::ping_devices () } // go to the previous track. -// Assume that get_sorted_routes().size() > route_table.size() void MackieControlProtocol::prev_track() { @@ -219,152 +219,128 @@ MackieControlProtocol::prev_track() } // go to the next track. -// Assume that get_sorted_routes().size() > route_table.size() void MackieControlProtocol::next_track() { - Sorted sorted = get_sorted_routes(); - if (_current_initial_bank + n_strips() < sorted.size()) { + Sorted sorted = get_sorted_stripables(); + if (_current_initial_bank + 1 < sorted.size()) { switch_banks (_current_initial_bank + 1); } } bool -MackieControlProtocol::route_is_locked_to_strip (boost::shared_ptr r) const +MackieControlProtocol::stripable_is_locked_to_strip (boost::shared_ptr r) const { for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) { - if ((*si)->route_is_locked_to_strip (r)) { + if ((*si)->stripable_is_locked_to_strip (r)) { return true; } } return false; } -// predicate for sort call in get_sorted_routes -struct RouteByRemoteId +// predicate for sort call in get_sorted_stripables +struct StripableByPresentationOrder { - bool operator () (const boost::shared_ptr & a, const boost::shared_ptr & b) const + bool operator () (const boost::shared_ptr & a, const boost::shared_ptr & b) const { - return a->remote_control_id() < b->remote_control_id(); + return a->presentation_info().order() < b->presentation_info().order(); } - bool operator () (const Route & a, const Route & b) const + bool operator () (const Stripable & a, const Stripable & b) const { - return a.remote_control_id() < b.remote_control_id(); + return a.presentation_info().order() < b.presentation_info().order(); } - bool operator () (const Route * a, const Route * b) const + bool operator () (const Stripable * a, const Stripable * b) const { - return a->remote_control_id() < b->remote_control_id(); + return a->presentation_info().order() < b->presentation_info().order(); } }; MackieControlProtocol::Sorted -MackieControlProtocol::get_sorted_routes() +MackieControlProtocol::get_sorted_stripables() { Sorted sorted; - // fetch all routes - boost::shared_ptr routes = session->get_routes(); - set remote_ids; - - // routes with remote_id 0 should never be added - // TODO verify this with ardour devs - // remote_ids.insert (0); + // fetch all stripables + StripableList stripables; - // sort in remote_id order, and exclude master, control and hidden routes - // and any routes that are already set. + session->get_stripables (stripables); - for (RouteList::iterator it = routes->begin(); it != routes->end(); ++it) { + // sort in presentation order, and exclude master, control and hidden stripables + // and any stripables that are already set. - boost::shared_ptr route = *it; + for (StripableList::iterator it = stripables.begin(); it != stripables.end(); ++it) { - if (remote_ids.find (route->remote_control_id()) != remote_ids.end()) { - continue; - } + boost::shared_ptr s = *it; - if (route->is_auditioner() || route->is_master() || route->is_monitor()) { + if (s->presentation_info().special()) { continue; } /* don't include locked routes */ - if (route_is_locked_to_strip(route)) { + if (stripable_is_locked_to_strip (s)) { continue; } switch (_view_mode) { case Mixer: - if (route->route_group()) { - route->route_group()->set_active (true, this); + if (!s->presentation_info().hidden()) { + sorted.push_back (s); } - sorted.push_back (route); - remote_ids.insert (route->remote_control_id()); break; case AudioTracks: - if (is_audio_track(route)) { - if (route->route_group()) { - route->route_group()->set_active (true, this); - } - sorted.push_back (route); - remote_ids.insert (route->remote_control_id()); + if (is_audio_track(s) && !s->presentation_info().hidden()) { + sorted.push_back (s); } break; case Busses: if (Profile->get_mixbus()) { #ifdef MIXBUS - if (route->mixbus()) { - sorted.push_back (route); - remote_ids.insert (route->remote_control_id()); + if (s->mixbus()) { + sorted.push_back (s); } -#endif +#endif } else { - if (!is_track(route)) { - if (route->route_group()) { - route->route_group()->set_active (true, this); - } - sorted.push_back (route); - remote_ids.insert (route->remote_control_id()); + if (!is_track(s) && !s->presentation_info().hidden()) { + sorted.push_back (s); } } break; case MidiTracks: - if (is_midi_track(route)) { - if (route->route_group()) { - route->route_group()->set_active (true, this); - } - sorted.push_back (route); - remote_ids.insert (route->remote_control_id()); + if (is_midi_track(s) && !s->presentation_info().hidden()) { + sorted.push_back (s); } break; case Plugins: break; - case Auxes: // in ardour, for now aux and buss are same. for mixbus, see "Busses" case above - if (!is_track(route)) { - if (route->route_group()) { - route->route_group()->set_active (true, this); - } - sorted.push_back (route); - remote_ids.insert (route->remote_control_id()); + case Auxes: // in ardour, for now aux and buss are same. for mixbus, "Busses" are mixbuses, "Auxes" are ardour buses +#ifdef MIXBUS + if (!s->mixbus() && !is_track(s) && !s->presentation_info().hidden()) +#else + if (!is_track(s) && !s->presentation_info().hidden()) +#endif + { + sorted.push_back (s); } break; - case Selected: // For example: a group - if (selected(route)) { - /* Selected may be a group in which case we want to - * control each track separately. - */ - if (route->route_group()) { - route->route_group()->set_active (false, this); - } - sorted.push_back (route); - remote_ids.insert (route->remote_control_id()); + case Hidden: // Show all the tracks we have hidden + if (s->presentation_info().hidden()) { + // maybe separate groups + sorted.push_back (s); + } + break; + case Selected: // For example: a group (this is USER) + if (s->is_selected() && !s->presentation_info().hidden()) { + sorted.push_back (s); } break; } - } - sort (sorted.begin(), sorted.end(), RouteByRemoteId()); + sort (sorted.begin(), sorted.end(), StripableByPresentationOrder()); return sorted; } @@ -386,62 +362,82 @@ MackieControlProtocol::n_strips (bool with_locked_strips) const return strip_count; } -void +int MackieControlProtocol::switch_banks (uint32_t initial, bool force) { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch banking to start at %1 force ? %2 current = %3\n", initial, force, _current_initial_bank)); if (initial == _current_initial_bank && !force) { - return; + /* everything is as it should be */ + return 0; } - Sorted sorted = get_sorted_routes(); + Sorted sorted = get_sorted_stripables(); uint32_t strip_cnt = n_strips (false); // do not include locked strips // in this count + if (initial >= sorted.size() && !force) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("bank target %1 exceeds route range %2\n", + _current_initial_bank, sorted.size())); + /* too high, we can't get there */ + return -1; + } + if (sorted.size() <= strip_cnt && _current_initial_bank == 0 && !force) { - /* no banking - not enough routes to fill all strips and we're + /* no banking - not enough stripables to fill all strips and we're * not at the first one. */ - return; + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("less routes (%1) than strips (%2) and we're at the end already (%3)\n", + sorted.size(), strip_cnt, _current_initial_bank)); + return -1; } + _current_initial_bank = initial; _current_selected_track = -1; - // Map current bank of routes onto each surface(+strip) + // Map current bank of stripables onto each surface(+strip) - if (_current_initial_bank <= sorted.size()) { + if (_current_initial_bank < sorted.size()) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2, available routes %3 on %4 surfaces\n", + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2, available stripables %3 on %4 surfaces\n", _current_initial_bank, strip_cnt, sorted.size(), surfaces.size())); - // link routes to strips + // link stripables to strips Sorted::iterator r = sorted.begin() + _current_initial_bank; for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) { - vector > routes; + vector > stripables; uint32_t added = 0; - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface has %1 unlockedstrips\n", (*si)->n_strips (false))); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface has %1 unlocked strips\n", (*si)->n_strips (false))); for (; r != sorted.end() && added < (*si)->n_strips (false); ++r, ++added) { - routes.push_back (*r); + stripables.push_back (*r); } - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("give surface %1 routes\n", routes.size())); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("give surface %1 stripables\n", stripables.size())); - (*si)->map_routes (routes); + (*si)->map_stripables (stripables); } - } - - /* make sure selection is correct */ - _gui_track_selection_changed (&_last_selected_routes, false, false); + } else { + /* all strips need to be reset */ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("clear all strips, bank target %1 is outside route range %2\n", + _current_initial_bank, sorted.size())); + for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + vector > stripables; + /* pass in an empty stripables list, so that all strips will be reset */ + (*si)->map_stripables (stripables); + } + return -1; + } /* current bank has not been saved */ session->set_dirty(); + + return 0; } int @@ -465,16 +461,28 @@ MackieControlProtocol::set_active (bool yn) set_device (_device_info.name(), true); } - /* set up periodic task for metering and automation + /* set up periodic task for timecode display and metering and automation */ - Glib::RefPtr periodic_timeout = Glib::TimeoutSource::create (100); // milliseconds + // set different refresh time for qcon and standard mackie MCU + + int iTimeCodeRefreshTime = 100; // default value for mackie MCU (100ms) + int iStripDisplayRefreshTime = 10; // default value for Mackie MCU (10ms) + + if(_device_info.is_qcon()){ + // set faster timecode display refresh speed (55ms) + iTimeCodeRefreshTime = 55; + // set slower refresh time on qcon than on mackie (15ms) + iStripDisplayRefreshTime = 15; + } + + Glib::RefPtr periodic_timeout = Glib::TimeoutSource::create (iTimeCodeRefreshTime); // milliseconds periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::periodic)); periodic_timeout->attach (main_loop()->get_context()); - /* a faster periodic task used to display parameter updates */ + /* periodic task used to update strip displays */ - Glib::RefPtr redisplay_timeout = Glib::TimeoutSource::create (10); // milliseconds + Glib::RefPtr redisplay_timeout = Glib::TimeoutSource::create (iStripDisplayRefreshTime); // milliseconds redisplay_connection = redisplay_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::redisplay)); redisplay_timeout->attach (main_loop()->get_context()); @@ -511,14 +519,14 @@ MackieControlProtocol::periodic () return false; } - if (needs_ipmidi_restart) { - ipmidi_restart (); + if (!_initialized) { + /* wait for higher-frequency redisplay() callback to initialize + * us + */ return true; } - if (!_initialized) { - initialize(); - } + update_timecode_display (); ARDOUR::microseconds_t now_usecs = ARDOUR::get_microseconds (); @@ -530,8 +538,6 @@ MackieControlProtocol::periodic () } } - update_timecode_display (); - return true; } @@ -557,7 +563,7 @@ MackieControlProtocol::redisplay () Glib::Threads::Mutex::Lock lm (surfaces_lock); for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - (*s)->redisplay (now); + (*s)->redisplay (now, false); } } @@ -644,14 +650,10 @@ MackieControlProtocol::update_global_led (int id, LedState ls) void MackieControlProtocol::device_ready () { - /* this is not required to be called, but for devices which do - * handshaking, it can be called once the device has verified the - * connection. - */ - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("device ready init (active=%1)\n", active())); update_surfaces (); - set_pot_mode (_pot_mode); + set_subview_mode (MackieControlProtocol::None, boost::shared_ptr()); + set_flip_mode (Normal); } // send messages to surface to set controls to correct values @@ -665,7 +667,7 @@ MackieControlProtocol::update_surfaces() // do the initial bank switch to connect signals // _current_initial_bank is initialised by set_state - switch_banks (_current_initial_bank, true); + (void) switch_banks (_current_initial_bank, true); DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::update_surfaces() finished\n"); } @@ -703,8 +705,10 @@ void MackieControlProtocol::connect_session_signals() { // receive routes added - session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_route_added, this, _1), this); - session->RouteAddedOrRemoved.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_route_added_or_removed, this), this); + session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this); + // receive VCAs added + session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_vca_added, this, _1), this); + // receive record state toggled session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_record_state_changed, this), this); // receive transport state changed @@ -717,12 +721,8 @@ MackieControlProtocol::connect_session_signals() session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_solo_active_changed, this, _1), this); // make sure remote id changed signals reach here - // see also notify_route_added - Sorted sorted = get_sorted_routes(); - - for (Sorted::iterator it = sorted.begin(); it != sorted.end(); ++it) { - (*it)->RemoteControlIDChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_remote_id_changed, this), this); - } + // see also notify_stripable_added + Sorted sorted = get_sorted_stripables(); } void @@ -867,7 +867,7 @@ MackieControlProtocol::create_surfaces () XMLNode* this_device = 0; XMLNodeList const& devices = configuration_state->children(); for (XMLNodeList::const_iterator d = devices.begin(); d != devices.end(); ++d) { - XMLProperty* prop = (*d)->property (X_("name")); + XMLProperty const * prop = (*d)->property (X_("name")); if (prop && prop->value() == _device_info.name()) { this_device = *d; break; @@ -965,7 +965,7 @@ MackieControlProtocol::close() { port_connection.disconnect (); session_connections.drop_connections (); - route_connections.drop_connections (); + stripable_connections.drop_connections (); periodic_connection.disconnect (); clear_surfaces(); @@ -985,7 +985,7 @@ MackieControlProtocol::update_configuration_state () } XMLNode* devnode = new XMLNode (X_("Configuration")); - devnode->add_property (X_("name"), _device_info.name()); + devnode->set_property (X_("name"), _device_info.name()); configuration_state->remove_nodes_and_delete (X_("name"), _device_info.name()); configuration_state->add_child_nocopy (*devnode); @@ -1005,18 +1005,15 @@ MackieControlProtocol::get_state() XMLNode& node (ControlProtocol::get_state()); DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::get_state init\n"); - char buf[16]; // add current bank - snprintf (buf, sizeof (buf), "%d", _current_initial_bank); - node.add_property (X_("bank"), buf); + node.set_property (X_("bank"), _current_initial_bank); // ipMIDI base port (possibly not used) - snprintf (buf, sizeof (buf), "%d", _ipmidi_base); - node.add_property (X_("ipmidi-base"), buf); + node.set_property (X_("ipmidi-base"), _ipmidi_base); - node.add_property (X_("device-profile"), _device_profile.name()); - node.add_property (X_("device-name"), _device_info.name()); + node.set_property (X_("device-profile"), _device_profile.name()); + node.set_property (X_("device-name"), _device_info.name()); { Glib::Threads::Mutex::Lock lm (surfaces_lock); @@ -1031,43 +1028,72 @@ MackieControlProtocol::get_state() return node; } +bool +MackieControlProtocol::profile_exists (string const & name) const +{ + return DeviceProfile::device_profiles.find (name) != DeviceProfile::device_profiles.end(); +} + int MackieControlProtocol::set_state (const XMLNode & node, int version) { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::set_state: active %1\n", active())); - int retval = 0; - const XMLProperty* prop; - uint32_t bank = 0; - if (ControlProtocol::set_state (node, version)) { return -1; } - if ((prop = node.property (X_("ipmidi-base"))) != 0) { - set_ipmidi_base (atoi (prop->value())); + uint16_t ipmidi_base; + if (node.get_property (X_("ipmidi-base"), ipmidi_base)) { + set_ipmidi_base (ipmidi_base); } + uint32_t bank = 0; // fetch current bank - if ((prop = node.property (X_("bank"))) != 0) { - bank = atoi (prop->value()); - } + node.get_property (X_("bank"), bank); - if ((prop = node.property (X_("device-name"))) != 0) { - set_device_info (prop->value()); + std::string device_name; + if (node.get_property (X_("device-name"), device_name)) { + set_device_info (device_name); } - if ((prop = node.property (X_("device-profile"))) != 0) { - if (prop->value().empty()) { + std::string device_profile_name; + if (node.get_property (X_("device-profile"), device_profile_name)) { + if (device_profile_name.empty()) { string default_profile_name; - default_profile_name = Glib::get_user_name(); - default_profile_name += ' '; - default_profile_name += _device_info.name(); + /* start by looking for a user-edited profile for the current device name */ + + default_profile_name = DeviceProfile::name_when_edited (_device_info.name()); + + if (!profile_exists (default_profile_name)) { + + /* no user-edited profile for this device name, so try the user-edited default profile */ + + default_profile_name = DeviceProfile::name_when_edited (DeviceProfile::default_profile_name); + + if (!profile_exists (default_profile_name)) { + + /* no user-edited version, so just try the device name */ + + default_profile_name = _device_info.name(); + + if (!profile_exists (default_profile_name)) { + + /* no generic device specific profile, just try the fixed default */ + default_profile_name = DeviceProfile::default_profile_name; + } + } + } set_profile (default_profile_name); + } else { - set_profile (prop->value()); + if (profile_exists (device_profile_name)) { + set_profile (device_profile_name); + } else { + set_profile (DeviceProfile::default_profile_name); + } } } @@ -1081,19 +1107,19 @@ MackieControlProtocol::set_state (const XMLNode & node, int version) state_version = version; } - switch_banks (bank, true); + (void) switch_banks (bank, true); DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::set_state done\n"); - return retval; + return 0; } string -MackieControlProtocol::format_bbt_timecode (framepos_t now_frame) +MackieControlProtocol::format_bbt_timecode (samplepos_t now_sample) { Timecode::BBT_Time bbt_time; - session->bbt_time (now_frame, bbt_time); + session->bbt_time (now_sample, bbt_time); // The Mackie protocol spec is built around a BBT time display of // @@ -1116,14 +1142,14 @@ MackieControlProtocol::format_bbt_timecode (framepos_t now_frame) } string -MackieControlProtocol::format_timecode_timecode (framepos_t now_frame) +MackieControlProtocol::format_timecode_timecode (samplepos_t now_sample) { Timecode::Time timecode; - session->timecode_time (now_frame, timecode); + session->timecode_time (now_sample, timecode); // According to the Logic docs // digits: 888/88/88/888 - // Timecode mode: Hours/Minutes/Seconds/Frames + // Timecode mode: Hours/Minutes/Seconds/Samples ostringstream os; os << setw(2) << setfill('0') << timecode.hours; os << ' '; @@ -1150,23 +1176,23 @@ MackieControlProtocol::update_timecode_display() return; } - // do assignment here so current_frame is fixed - framepos_t current_frame = session->transport_frame(); + // do assignment here so current_sample is fixed + samplepos_t current_sample = session->transport_sample(); string timecode; // For large jumps in play head possition do full reset - int moved = (current_frame - _frame_last) / session->frame_rate (); + int moved = (current_sample - _sample_last) / session->sample_rate (); if (moved) { DEBUG_TRACE (DEBUG::MackieControl, "Timecode reset\n"); _timecode_last = string (10, ' '); } - _frame_last = current_frame; + _sample_last = current_sample; switch (_timecode_type) { case ARDOUR::AnyTime::BBT: - timecode = format_bbt_timecode (current_frame); + timecode = format_bbt_timecode (current_sample); break; case ARDOUR::AnyTime::Timecode: - timecode = format_timecode_timecode (current_frame); + timecode = format_timecode_timecode (current_sample); break; default: return; @@ -1187,20 +1213,28 @@ MackieControlProtocol::update_timecode_display() void MackieControlProtocol::notify_parameter_changed (std::string const & p) { if (p == "punch-in") { - // no such button right now - // update_global_button (Button::PunchIn, session->config.get_punch_in()); + update_global_button (Button::Drop, session->config.get_punch_in() ? flashing : off); } else if (p == "punch-out") { - // no such button right now - // update_global_button (Button::PunchOut, session->config.get_punch_out()); + update_global_button (Button::Replace, session->config.get_punch_out() ? flashing : off); } else if (p == "clicking") { update_global_button (Button::Click, Config->get_clicking()); + } else if (p == "follow-edits") { + /* we can't respond to this at present, because "follow-edits" + * is a property of the (G)UI configuration object, to which we + * have no access. For now, this means that the lit state of + * this button (if there is one) won't reflect the setting. + */ + + //update_global_button (Button::Enter, session->config.get_follow_edits() ? on : off); + } else if (p == "external-sync") { + update_global_button (Button::Cancel, session->config.get_external_sync() ? on : off); } else { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("parameter changed: %1\n", p)); } } void -MackieControlProtocol::notify_route_added_or_removed () +MackieControlProtocol::notify_stripable_removed () { Glib::Threads::Mutex::Lock lm (surfaces_lock); for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { @@ -1208,9 +1242,15 @@ MackieControlProtocol::notify_route_added_or_removed () } } -// RouteList is the set of routes that have just been added void -MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl) +MackieControlProtocol::notify_vca_added (ARDOUR::VCAList& vl) +{ + refresh_current_bank (); +} + +// RouteList is the set of Routes that have just been added +void +MackieControlProtocol::notify_routes_added (ARDOUR::RouteList & rl) { { Glib::Threads::Mutex::Lock lm (surfaces_lock); @@ -1235,13 +1275,6 @@ MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl) refresh_current_bank(); // otherwise route added, but current bank needs no updating - - // make sure remote id changes in the new route are handled - typedef ARDOUR::RouteList ARS; - - for (ARS::iterator it = rl.begin(); it != rl.end(); ++it) { - (*it)->RemoteControlIDChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_remote_id_changed, this), this); - } } void @@ -1269,8 +1302,17 @@ MackieControlProtocol::notify_solo_active_changed (bool active) } void -MackieControlProtocol::notify_remote_id_changed() +MackieControlProtocol::notify_presentation_info_changed (PBD::PropertyChange const & what_changed) { + PBD::PropertyChange order_or_hidden; + + order_or_hidden.add (Properties::hidden); + order_or_hidden.add (Properties::order); + + if (!what_changed.contains (order_or_hidden)) { + return; + } + { Glib::Threads::Mutex::Lock lm (surfaces_lock); @@ -1279,19 +1321,7 @@ MackieControlProtocol::notify_remote_id_changed() } } - Sorted sorted = get_sorted_routes(); - uint32_t sz = n_strips(); - - // if a remote id has been moved off the end, we need to shift - // the current bank backwards. - - if (sorted.size() - _current_initial_bank < sz) { - // but don't shift backwards past the zeroth channel - switch_banks (max((Sorted::size_type) 0, sorted.size() - sz)); - } else { - // Otherwise just refresh the current bank - refresh_current_bank(); - } + refresh_current_bank(); } /////////////////////////////////////////// @@ -1369,8 +1399,21 @@ MackieControlProtocol::notify_record_state_changed () ls = on; break; case Session::Enabled: - DEBUG_TRACE (DEBUG::MackieControl, "record state changed to enabled, LED flashing\n"); - ls = flashing; + + if(_device_info.is_qcon()){ + // For qcon the rec button is two state only (on/off) + DEBUG_TRACE (DEBUG::MackieControl, "record state changed to enabled, LED on (QCon)\n"); + ls = on; + break; + + } + else{ + // For standard Mackie MCU the record LED is flashing + DEBUG_TRACE (DEBUG::MackieControl, "record state changed to enabled, LED flashing\n"); + ls = flashing; + break; + } + break; } @@ -1395,6 +1438,7 @@ MackieControlProtocol::bundles () void MackieControlProtocol::do_request (MackieControlUIRequest* req) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("doing request type %1\n", req->type)); if (req->type == CallSlot) { call_slot (MISSING_INVALIDATOR, req->the_slot); @@ -1443,14 +1487,14 @@ MackieControlProtocol::build_button_map () DEFINE_BUTTON_HANDLER (Button::View, &MackieControlProtocol::view_press, &MackieControlProtocol::view_release); DEFINE_BUTTON_HANDLER (Button::NameValue, &MackieControlProtocol::name_value_press, &MackieControlProtocol::name_value_release); DEFINE_BUTTON_HANDLER (Button::TimecodeBeats, &MackieControlProtocol::timecode_beats_press, &MackieControlProtocol::timecode_beats_release); - DEFINE_BUTTON_HANDLER (Button::F1, &MackieControlProtocol::F1_press, &MackieControlProtocol::F1_release); - DEFINE_BUTTON_HANDLER (Button::F2, &MackieControlProtocol::F2_press, &MackieControlProtocol::F2_release); - DEFINE_BUTTON_HANDLER (Button::F3, &MackieControlProtocol::F3_press, &MackieControlProtocol::F3_release); - DEFINE_BUTTON_HANDLER (Button::F4, &MackieControlProtocol::F4_press, &MackieControlProtocol::F4_release); - DEFINE_BUTTON_HANDLER (Button::F5, &MackieControlProtocol::F5_press, &MackieControlProtocol::F5_release); - DEFINE_BUTTON_HANDLER (Button::F6, &MackieControlProtocol::F6_press, &MackieControlProtocol::F6_release); - DEFINE_BUTTON_HANDLER (Button::F7, &MackieControlProtocol::F7_press, &MackieControlProtocol::F7_release); - DEFINE_BUTTON_HANDLER (Button::F8, &MackieControlProtocol::F8_press, &MackieControlProtocol::F8_release); +// DEFINE_BUTTON_HANDLER (Button::F1, &MackieControlProtocol::F1_press, &MackieControlProtocol::F1_release); +// DEFINE_BUTTON_HANDLER (Button::F2, &MackieControlProtocol::F2_press, &MackieControlProtocol::F2_release); +// DEFINE_BUTTON_HANDLER (Button::F3, &MackieControlProtocol::F3_press, &MackieControlProtocol::F3_release); +// DEFINE_BUTTON_HANDLER (Button::F4, &MackieControlProtocol::F4_press, &MackieControlProtocol::F4_release); +// DEFINE_BUTTON_HANDLER (Button::F5, &MackieControlProtocol::F5_press, &MackieControlProtocol::F5_release); +// DEFINE_BUTTON_HANDLER (Button::F6, &MackieControlProtocol::F6_press, &MackieControlProtocol::F6_release); +// DEFINE_BUTTON_HANDLER (Button::F7, &MackieControlProtocol::F7_press, &MackieControlProtocol::F7_release); +// DEFINE_BUTTON_HANDLER (Button::F8, &MackieControlProtocol::F8_press, &MackieControlProtocol::F8_release); DEFINE_BUTTON_HANDLER (Button::MidiTracks, &MackieControlProtocol::miditracks_press, &MackieControlProtocol::miditracks_release); DEFINE_BUTTON_HANDLER (Button::Inputs, &MackieControlProtocol::inputs_press, &MackieControlProtocol::inputs_release); DEFINE_BUTTON_HANDLER (Button::AudioTracks, &MackieControlProtocol::audiotracks_press, &MackieControlProtocol::audiotracks_release); @@ -1513,6 +1557,8 @@ MackieControlProtocol::handle_button_event (Surface& surface, Button& button, Bu string action = _device_profile.get_button_action (button.bid(), _modifier_state); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("device profile returned [%1] for that button\n", action)); + if (!action.empty()) { if (action.find ('/') != string::npos) { /* good chance that this is really an action */ @@ -1526,10 +1572,12 @@ MackieControlProtocol::handle_button_event (Surface& surface, Button& button, Bu occur either. */ if (bs == press) { + update_led (surface, button, on); DEBUG_TRACE (DEBUG::MackieControl, string_compose ("executing action %1\n", action)); access_action (action); + } else { + update_led (surface, button, off); } - return; } else { @@ -1553,14 +1601,30 @@ MackieControlProtocol::handle_button_event (Surface& surface, Button& button, Bu } } + /* Now that we have the correct (maybe remapped) button ID, do these + * checks on it. + */ + + if ((button_id != Button::Marker) && (modifier_state() & MODIFIER_MARKER)) { + marker_modifier_consumed_by_button = true; + } + + if ((button_id != Button::Nudge) && (modifier_state() & MODIFIER_NUDGE)) { + nudge_modifier_consumed_by_button = true; + } + /* lookup using the device-INDEPENDENT button ID */ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("now looking up button ID %1\n", button_id)); + ButtonMap::iterator b = button_map.find (button_id); if (b != button_map.end()) { ButtonHandlers& bh (b->second); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("button found in map, now invoking %1\n", (bs == press ? "press" : "release"))); + switch (bs) { case press: surface.write (button.set_state ((this->*(bh.press)) (button))); @@ -1589,7 +1653,7 @@ MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port) if (ioc & IO_IN) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("something happend on %1\n", port->name())); + // DEBUG_TRACE (DEBUG::MackieControl, string_compose ("something happend on %1\n", port->name())); /* Devices using regular JACK MIDI ports will need to have the x-thread FIFO drained to avoid burning endless CPU. @@ -1606,8 +1670,8 @@ MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port) } } - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("data available on %1\n", port->name())); - framepos_t now = session->engine().sample_time(); + // DEBUG_TRACE (DEBUG::MackieControl, string_compose ("data available on %1\n", port->name())); + samplepos_t now = session->engine().sample_time(); port->parse (now); } @@ -1624,183 +1688,248 @@ MackieControlProtocol::clear_ports () } void -MackieControlProtocol::notify_subview_route_deleted () +MackieControlProtocol::notify_subview_stripable_deleted () { /* return to global/mixer view */ - _subview_route.reset (); + _subview_stripable.reset (); set_view_mode (Mixer); } -void -MackieControlProtocol::set_subview_mode (SubViewMode sm, boost::shared_ptr r) +bool +MackieControlProtocol::subview_mode_would_be_ok (SubViewMode mode, boost::shared_ptr r) { - SubViewMode old_mode = _subview_mode; - boost::shared_ptr old_route = _subview_route; - - _subview_mode = sm; - - if (r) { - /* retain _subview_route even if it is reset to null implicitly */ - _subview_route = r; - } - - if ((_subview_mode != old_mode) || (_subview_route != old_route)) { + switch (mode) { + case None: + return true; + break; - if (r != old_route) { - subview_route_connections.drop_connections (); - if (_subview_route) { - _subview_route->DropReferences.connect (subview_route_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_subview_route_deleted, this), this); - } + case Sends: + if (r && r->send_level_controllable (0)) { + return true; } + break; - /* subview mode did actually change */ - - { - Surfaces copy; /* can't hold surfaces lock while calling Strip::subview_mode_changed */ - - { - Glib::Threads::Mutex::Lock lm (surfaces_lock); - copy = surfaces; - } - - for (Surfaces::iterator s = copy.begin(); s != copy.end(); ++s) { - (*s)->subview_mode_changed (); - } + case EQ: + if (r && r->eq_band_cnt() > 0) { + return true; } + break; - if (_subview_mode != old_mode) { - - /* turn buttons related to vpot mode on or off as required */ + case Dynamics: + if (r && r->comp_enable_controllable()) { + return true; + } + break; - switch (_subview_mode) { - case MackieControlProtocol::None: - pot_mode_globals (); - break; - case MackieControlProtocol::EQ: - update_global_button (Button::Eq, on); - update_global_button (Button::Dyn, off); - update_global_button (Button::AudioInstruments, off); /* faking up Dyn */ - update_global_button (Button::Trim, off); - update_global_button (Button::Send, off); - update_global_button (Button::Pan, off); - break; - case MackieControlProtocol::Dynamics: - update_global_button (Button::Eq, off); - update_global_button (Button::Dyn, on); - update_global_button (Button::AudioInstruments, on); /* faking up Dyn */ - update_global_button (Button::Trim, off); - update_global_button (Button::Send, off); - update_global_button (Button::Pan, off); - break; - } + case TrackView: + if (r) { + return true; } } -} - -void -MackieControlProtocol::set_view_mode (ViewMode m) -{ - _last_bank[_view_mode] = _current_initial_bank; - _view_mode = m; - set_subview_mode (None, boost::shared_ptr()); - - switch_banks(_last_bank[_view_mode], true); + return false; } -void -MackieControlProtocol::display_view_mode () +bool +MackieControlProtocol::redisplay_subview_mode () { + Surfaces copy; /* can't hold surfaces lock while calling Strip::subview_mode_changed */ + { Glib::Threads::Mutex::Lock lm (surfaces_lock); + copy = surfaces; + } - for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - (*s)->update_view_mode_display (); - } + for (Surfaces::iterator s = copy.begin(); s != copy.end(); ++s) { + (*s)->subview_mode_changed (); } + + /* don't call this again from a timeout */ + return false; } -void -MackieControlProtocol::set_flip_mode (FlipMode fm) +int +MackieControlProtocol::set_subview_mode (SubViewMode sm, boost::shared_ptr r) { - if (_flip_mode != fm) { - if (fm == Normal) { - update_global_button (Button::Flip, off); - } else { - update_global_button (Button::Flip, on); - } + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("set subview mode %1 with stripable %2, current flip mode %3\n", sm, (r ? r->name() : string ("null")), _flip_mode)); - Glib::Threads::Mutex::Lock lm (surfaces_lock); + if (_flip_mode != Normal) { + set_flip_mode (Normal); + } - _flip_mode = fm; + if (!subview_mode_would_be_ok (sm, r)) { - for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - (*s)->update_flip_mode_display (); + DEBUG_TRACE (DEBUG::MackieControl, "subview mode not OK\n"); + + if (r) { + + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + if (!surfaces.empty()) { + + string msg; + + switch (sm) { + case Sends: + msg = _("no sends for selected track/bus"); + break; + case EQ: + msg = _("no EQ in the track/bus"); + break; + case Dynamics: + msg = _("no dynamics in selected track/bus"); + break; + case TrackView: + msg = _("no track view possible"); + default: + break; + } + if (!msg.empty()) { + surfaces.front()->display_message_for (msg, 1000); + if (_subview_mode != None) { + /* redisplay current subview mode after + that message goes away. + */ + Glib::RefPtr redisplay_timeout = Glib::TimeoutSource::create (1000); // milliseconds + redisplay_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::redisplay_subview_mode)); + redisplay_timeout->attach (main_loop()->get_context()); + } + } + } } - } -} -void -MackieControlProtocol::set_pot_mode (PotMode m) -{ - // maybe not in flip mode. - if (flip_mode()) { - return; + return -1; } - _pot_mode = m; + boost::shared_ptr old_stripable = _subview_stripable; - { - Glib::Threads::Mutex::Lock lm (surfaces_lock); + _subview_mode = sm; + _subview_stripable = r; - for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - (*s)->update_potmode (); + if (_subview_stripable != old_stripable) { + subview_stripable_connections.drop_connections (); + /* Catch the current subview stripable going away */ + if (_subview_stripable) { + _subview_stripable->DropReferences.connect (subview_stripable_connections, MISSING_INVALIDATOR, + boost::bind (&MackieControlProtocol::notify_subview_stripable_deleted, this), + this); } } - pot_mode_globals (); -} + redisplay_subview_mode (); -void -MackieControlProtocol::pot_mode_globals () -{ - update_global_button (Button::Eq, off); - update_global_button (Button::Dyn, off); - update_global_button (Button::AudioInstruments, off); + /* turn buttons related to vpot mode on or off as required */ - switch (_pot_mode) { - case Trim: - update_global_button (Button::Track, on); + switch (_subview_mode) { + case MackieControlProtocol::None: update_global_button (Button::Send, off); + update_global_button (Button::Plugin, off); + update_global_button (Button::Eq, off); + update_global_button (Button::Dyn, off); + update_global_button (Button::Track, off); + update_global_button (Button::Pan, on); + break; + case MackieControlProtocol::EQ: + update_global_button (Button::Send, off); + update_global_button (Button::Plugin, off); + update_global_button (Button::Eq, on); + update_global_button (Button::Dyn, off); + update_global_button (Button::Track, off); update_global_button (Button::Pan, off); break; - case Send: + case MackieControlProtocol::Dynamics: + update_global_button (Button::Send, off); + update_global_button (Button::Plugin, off); + update_global_button (Button::Eq, off); + update_global_button (Button::Dyn, on); update_global_button (Button::Track, off); - update_global_button (Button::Send, on); update_global_button (Button::Pan, off); break; - case Pan: + case MackieControlProtocol::Sends: + update_global_button (Button::Send, on); + update_global_button (Button::Plugin, off); + update_global_button (Button::Eq, off); + update_global_button (Button::Dyn, off); update_global_button (Button::Track, off); + update_global_button (Button::Pan, off); + break; + case MackieControlProtocol::TrackView: update_global_button (Button::Send, off); - update_global_button (Button::Pan, on); - }; + update_global_button (Button::Plugin, off); + update_global_button (Button::Eq, off); + update_global_button (Button::Dyn, off); + update_global_button (Button::Track, on); + update_global_button (Button::Pan, off); + break; + } + + return 0; +} + +void +MackieControlProtocol::set_view_mode (ViewMode m) +{ + if (_flip_mode != Normal) { + set_flip_mode (Normal); + } + ViewMode old_view_mode = _view_mode; + + _view_mode = m; + _last_bank[old_view_mode] = _current_initial_bank; + + if (switch_banks(_last_bank[m], true)) { + _view_mode = old_view_mode; + return; + } + + /* leave subview mode, whatever it was */ + set_subview_mode (None, boost::shared_ptr()); + display_view_mode (); +} + +void +MackieControlProtocol::display_view_mode () +{ + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->update_view_mode_display (true); + } +} + +void +MackieControlProtocol::set_flip_mode (FlipMode fm) +{ + if (fm == Normal) { + update_global_button (Button::Flip, off); + } else { + update_global_button (Button::Flip, on); + } + + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + _flip_mode = fm; + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->update_flip_mode_display (); + } } void MackieControlProtocol::set_master_on_surface_strip (uint32_t surface, uint32_t strip_number) { - force_special_route_to_strip (session->master_out(), surface, strip_number); + force_special_stripable_to_strip (session->master_out(), surface, strip_number); } void MackieControlProtocol::set_monitor_on_surface_strip (uint32_t surface, uint32_t strip_number) { - force_special_route_to_strip (session->monitor_out(), surface, strip_number); + force_special_stripable_to_strip (session->monitor_out(), surface, strip_number); } void -MackieControlProtocol::force_special_route_to_strip (boost::shared_ptr r, uint32_t surface, uint32_t strip_number) +MackieControlProtocol::force_special_stripable_to_strip (boost::shared_ptr r, uint32_t surface, uint32_t strip_number) { if (!r) { return; @@ -1812,7 +1941,7 @@ MackieControlProtocol::force_special_route_to_strip (boost::shared_ptr r, if ((*s)->number() == surface) { Strip* strip = (*s)->nth_strip (strip_number); if (strip) { - strip->set_route (session->master_out()); + strip->set_stripable (session->master_out()); strip->lock_controls (); } } @@ -1820,54 +1949,93 @@ MackieControlProtocol::force_special_route_to_strip (boost::shared_ptr r, } void -MackieControlProtocol::gui_track_selection_changed (ARDOUR::RouteNotificationListPtr rl, bool save_list) +MackieControlProtocol::check_fader_automation_state () { - _gui_track_selection_changed (rl.get(), save_list, true); -} + fader_automation_connections.drop_connections (); -void -MackieControlProtocol::_gui_track_selection_changed (ARDOUR::RouteNotificationList* rl, bool save_list, bool gui_selection_did_change) -{ - /* We need to keep a list of the most recently selected routes around, - but we are not allowed to keep shared_ptr unless we want to - handle the complexities of route deletion. So instead, the GUI sends - us a notification using weak_ptr, which we keep a copy - of. For efficiency's sake, however, we convert the weak_ptr's into - shared_ptr before passing them to however many surfaces (and - thus strips) that we have. - */ - - StrongRouteNotificationList srl; + boost::shared_ptr r = first_selected_stripable (); - for (ARDOUR::RouteNotificationList::const_iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr r = (*i).lock(); - if (r) { - srl.push_back (r); - } + if (!r) { + update_global_button (Button::Read, off); + update_global_button (Button::Write, off); + update_global_button (Button::Touch, off); + update_global_button (Button::Trim, off); + update_global_button (Button::Latch, off); + update_global_button (Button::Grp, on); + return; } - { - Glib::Threads::Mutex::Lock lm (surfaces_lock); + r->gain_control()->alist()->automation_state_changed.connect (fader_automation_connections, + MISSING_INVALIDATOR, + boost::bind (&MackieControlProtocol::update_fader_automation_state, this), + this); - for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - (*s)->gui_selection_changed (srl); - } - } + update_fader_automation_state (); +} + +void +MackieControlProtocol::update_fader_automation_state () +{ + boost::shared_ptr r = first_selected_stripable (); - if (save_list) { - _last_selected_routes = *rl; + if (!r) { + update_global_button (Button::Read, off); + update_global_button (Button::Write, off); + update_global_button (Button::Touch, off); + update_global_button (Button::Trim, off); + update_global_button (Button::Latch, off); + update_global_button (Button::Grp, on); + return; } - if (gui_selection_did_change) { - /* actual GUI selection changed */ - set_subview_mode (_subview_mode, first_selected_route()); + switch (r->gain_control()->automation_state()) { + case Off: + update_global_button (Button::Read, off); + update_global_button (Button::Write, off); + update_global_button (Button::Touch, off); + update_global_button (Button::Trim, off); + update_global_button (Button::Latch, off); + update_global_button (Button::Grp, on); + break; + case Play: + update_global_button (Button::Read, on); + update_global_button (Button::Write, off); + update_global_button (Button::Touch, off); + update_global_button (Button::Trim, off); + update_global_button (Button::Latch, off); + update_global_button (Button::Grp, off); + break; + case Write: + update_global_button (Button::Read, off); + update_global_button (Button::Write, on); + update_global_button (Button::Touch, off); + update_global_button (Button::Trim, off); + update_global_button (Button::Latch, off); + update_global_button (Button::Grp, off); + break; + case Touch: + update_global_button (Button::Read, off); + update_global_button (Button::Write, off); + update_global_button (Button::Touch, on); + update_global_button (Button::Trim, off); + update_global_button (Button::Latch, off); + update_global_button (Button::Grp, off); + break; + case Latch: + update_global_button (Button::Read, off); + update_global_button (Button::Write, off); + update_global_button (Button::Touch, off); + update_global_button (Button::Trim, off); + update_global_button (Button::Latch, on); + update_global_button (Button::Grp, off); + break; } } -framepos_t -MackieControlProtocol::transport_frame() const +samplepos_t +MackieControlProtocol::transport_sample() const { - return session->transport_frame(); + return session->transport_sample(); } void @@ -1890,24 +2058,32 @@ MackieControlProtocol::remove_down_select_button (int surface, int strip) } void -MackieControlProtocol::select_range () +MackieControlProtocol::select_range (uint32_t pressed) { - RouteList routes; + StripableList stripables; - pull_route_range (_down_select_buttons, routes); + pull_stripable_range (_down_select_buttons, stripables, pressed); - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("select range: found %1 routes\n", routes.size())); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("select range: found %1 stripables, first = %2\n", stripables.size(), + (stripables.empty() ? "null" : stripables.front()->name()))); + + if (stripables.empty()) { + return; + } - if (!routes.empty()) { - for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { + if (stripables.size() == 1 && ControlProtocol::last_selected().size() == 1 && stripables.front()->is_selected()) { + /* cancel selection for one and only selected stripable */ + ToggleStripableSelection (stripables.front()); + } else { + for (StripableList::iterator s = stripables.begin(); s != stripables.end(); ++s) { - if (main_modifier_state() == MODIFIER_CONTROL) { - ToggleRouteSelection ((*r)->remote_control_id ()); + if (main_modifier_state() == MODIFIER_SHIFT) { + ToggleStripableSelection (*s); } else { - if (r == routes.begin()) { - SetRouteSelection ((*r)->remote_control_id()); + if (s == stripables.begin()) { + SetStripableSelection (*s); } else { - AddRouteToSelection ((*r)->remote_control_id()); + AddStripableToSelection (*s); } } } @@ -1949,10 +2125,10 @@ MackieControlProtocol::remove_down_button (AutomationType a, int surface, int st } MackieControlProtocol::ControlList -MackieControlProtocol::down_controls (AutomationType p) +MackieControlProtocol::down_controls (AutomationType p, uint32_t pressed) { ControlList controls; - RouteList routes; + StripableList stripables; DownButtonMap::iterator m = _down_buttons.find (p); @@ -1963,29 +2139,29 @@ MackieControlProtocol::down_controls (AutomationType p) DEBUG_TRACE (DEBUG::MackieControl, string_compose ("looking for down buttons for %1, got %2\n", p, m->second.size())); - pull_route_range (m->second, routes); + pull_stripable_range (m->second, stripables, pressed); switch (p) { case GainAutomation: - for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { - controls.push_back ((*r)->gain_control()); + for (StripableList::iterator s = stripables.begin(); s != stripables.end(); ++s) { + controls.push_back ((*s)->gain_control()); } break; case SoloAutomation: - for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { - controls.push_back ((*r)->solo_control()); + for (StripableList::iterator s = stripables.begin(); s != stripables.end(); ++s) { + controls.push_back ((*s)->solo_control()); } break; case MuteAutomation: - for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { - controls.push_back ((*r)->mute_control()); + for (StripableList::iterator s = stripables.begin(); s != stripables.end(); ++s) { + controls.push_back ((*s)->mute_control()); } break; case RecEnableAutomation: - for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { - boost::shared_ptr trk = boost::dynamic_pointer_cast (*r); - if (trk) { - controls.push_back (trk->rec_enable_control()); + for (StripableList::iterator s = stripables.begin(); s != stripables.end(); ++s) { + boost::shared_ptr ac = (*s)->rec_enable_control(); + if (ac) { + controls.push_back (ac); } } break; @@ -2006,7 +2182,7 @@ struct ButtonRangeSorter { }; void -MackieControlProtocol::pull_route_range (DownButtonList& down, RouteList& selected) +MackieControlProtocol::pull_stripable_range (DownButtonList& down, StripableList& selected, uint32_t pressed) { ButtonRangeSorter cmp; @@ -2056,13 +2232,19 @@ MackieControlProtocol::pull_route_range (DownButtonList& down, RouteList& select (*s)->number(), fs, ls)); for (uint32_t n = fs; n < ls; ++n) { - boost::shared_ptr r = (*s)->nth_strip (n)->route(); + Strip* strip = (*s)->nth_strip (n); + boost::shared_ptr r = strip->stripable(); if (r) { - selected.push_back (r); + if (global_index_locked (*strip) == pressed) { + selected.push_front (r); + } else { + selected.push_back (r); + } } } } } + } void @@ -2090,7 +2272,7 @@ MackieControlProtocol::ipmidi_restart () if (create_surfaces ()) { return -1; } - switch_banks (_current_initial_bank, true); + (void) switch_banks (_current_initial_bank, true); needs_ipmidi_restart = false; return 0; } @@ -2186,59 +2368,102 @@ MackieControlProtocol::connection_handler (boost::weak_ptr wp1, st } bool -MackieControlProtocol::is_track (boost::shared_ptr r) const +MackieControlProtocol::is_track (boost::shared_ptr r) const { return boost::dynamic_pointer_cast(r) != 0; } bool -MackieControlProtocol::is_audio_track (boost::shared_ptr r) const +MackieControlProtocol::is_audio_track (boost::shared_ptr r) const { return boost::dynamic_pointer_cast(r) != 0; } bool -MackieControlProtocol::is_midi_track (boost::shared_ptr r) const +MackieControlProtocol::is_midi_track (boost::shared_ptr r) const { return boost::dynamic_pointer_cast(r) != 0; } bool -MackieControlProtocol::selected (boost::shared_ptr r) const +MackieControlProtocol::is_mapped (boost::shared_ptr r) const { - const RouteNotificationList* rl = &_last_selected_routes; + Glib::Threads::Mutex::Lock lm (surfaces_lock); - for (ARDOUR::RouteNotificationList::const_iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr rt = (*i).lock(); - if (rt == r) { + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + if ((*s)->stripable_is_mapped (r)) { return true; } } + return false; } -boost::shared_ptr -MackieControlProtocol::first_selected_route () const +void +MackieControlProtocol::stripable_selection_changed () { - if (_last_selected_routes.empty()) { - return boost::shared_ptr(); + //this function is called after the stripable selection is "stable", so this is the place to check surface selection state + for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + (*si)->update_strip_selection (); } - boost::shared_ptr r = _last_selected_routes.front().lock(); + boost::shared_ptr s = first_selected_stripable (); + if (s) { + check_fader_automation_state (); + + /* It is possible that first_selected_route() may return null if we + * are no longer displaying/mapping that route. In that case, + * we will exit subview mode. If first_selected_route() is + * null, and subview mode is not None, then the first call to + * set_subview_mode() will fail, and we will reset to None. + */ - return r; /* may be null */ + if (set_subview_mode (_subview_mode, s)) { + set_subview_mode (None, boost::shared_ptr()); + } + + } } -boost::shared_ptr -MackieControlProtocol::subview_route () const +boost::shared_ptr +MackieControlProtocol::first_selected_stripable () const { - return _subview_route; + boost::shared_ptr s = ControlProtocol::first_selected_stripable(); + + if (s) { + /* check it is on one of our surfaces */ + + if (is_mapped (s)) { + return s; + } + + /* stripable is not mapped. thus, the currently selected stripable is + * not on the surfaces, and so from our perspective, there is + * no currently selected stripable. + */ + + s.reset (); + } + + return s; /* may be null */ +} + +boost::shared_ptr +MackieControlProtocol::subview_stripable () const +{ + return _subview_stripable; } uint32_t MackieControlProtocol::global_index (Strip& strip) { Glib::Threads::Mutex::Lock lm (surfaces_lock); + return global_index_locked (strip); +} + +uint32_t +MackieControlProtocol::global_index_locked (Strip& strip) +{ uint32_t global = 0; for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { @@ -2250,3 +2475,32 @@ MackieControlProtocol::global_index (Strip& strip) return global; } + +void* +MackieControlProtocol::request_factory (uint32_t num_requests) +{ + /* AbstractUI::request_buffer_factory() is a template method only + instantiated in this source module. To provide something visible for + use in the interface/descriptor, we have this static method that is + template-free. + */ + return request_buffer_factory (num_requests); +} + +void +MackieControlProtocol::set_automation_state (AutoState as) +{ + boost::shared_ptr r = first_selected_stripable (); + + if (!r) { + return; + } + + boost::shared_ptr ac = r->gain_control(); + + if (!ac) { + return; + } + + ac->set_automation_state (as); +}