X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libs%2Fsurfaces%2Fmackie%2Fmackie_control_protocol.cc;h=ea5e43d2babc30abb9e37204660bd4c4374c3383;hb=7cbf35a3d6b6c3cde001654478e79679c2e23088;hp=c83ce819d950edae1ce168b036ea4ffdddbfa03e;hpb=e279b9892b467aa823e253d97b6e9504cca0e252;p=ardour.git diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index c83ce819d9..ea5e43d2ba 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -31,6 +31,7 @@ #include #include +#include #include "midi++/types.h" #include "midi++/port.h" @@ -40,14 +41,19 @@ #include "pbd/memento_command.h" #include "pbd/convert.h" +#include "ardour/audio_track.h" #include "ardour/automation_control.h" +#include "ardour/async_midi_port.h" #include "ardour/dB.h" #include "ardour/debug.h" #include "ardour/location.h" #include "ardour/meter.h" +#include "ardour/midi_track.h" #include "ardour/panner.h" #include "ardour/panner_shell.h" +#include "ardour/profile.h" #include "ardour/route.h" +#include "ardour/route_group.h" #include "ardour/session.h" #include "ardour/tempo.h" #include "ardour/track.h" @@ -70,9 +76,10 @@ using namespace ARDOUR; using namespace std; -using namespace Mackie; using namespace PBD; using namespace Glib; +using namespace ArdourSurface; +using namespace Mackie; #include "i18n.h" @@ -82,6 +89,12 @@ const int MackieControlProtocol::MODIFIER_OPTION = 0x1; const int MackieControlProtocol::MODIFIER_CONTROL = 0x2; 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::MAIN_MODIFIER_MASK = (MackieControlProtocol::MODIFIER_OPTION| + MackieControlProtocol::MODIFIER_CONTROL| + MackieControlProtocol::MODIFIER_SHIFT| + MackieControlProtocol::MODIFIER_CMDALT); MackieControlProtocol* MackieControlProtocol::_instance = 0; @@ -92,28 +105,36 @@ bool MackieControlProtocol::probe() MackieControlProtocol::MackieControlProtocol (Session& session) : ControlProtocol (session, X_("Mackie")) - , AbstractUI ("mackie") + , AbstractUI (name()) , _current_initial_bank (0) + , _frame_last (0) , _timecode_type (ARDOUR::AnyTime::BBT) , _gui (0) - , _zoom_mode (false) , _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) , needs_ipmidi_restart (false) , _metering_active (true) , _initialized (false) - , _surfaces_state (0) - , _surfaces_version (0) + , configuration_state (0) + , state_version (0) { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::MackieControlProtocol\n"); DeviceInfo::reload_device_info (); DeviceProfile::reload_device_profiles (); + for (int i = 0; i < 9; i++) { + _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); _instance = this; @@ -124,14 +145,18 @@ MackieControlProtocol::MackieControlProtocol (Session& session) MackieControlProtocol::~MackieControlProtocol() { DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol init\n"); - + + for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + (*si)->reset (); + } + DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol drop_connections ()\n"); drop_connections (); DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol tear_down_gui ()\n"); tear_down_gui (); - delete _surfaces_state; + delete configuration_state; /* stop event loop */ DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol BaseUI::quit ()\n"); @@ -158,10 +183,10 @@ 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 */ @@ -172,22 +197,20 @@ MackieControlProtocol::thread_init () } void -MackieControlProtocol::midi_connectivity_established () +MackieControlProtocol::ping_devices () { - for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) { - (*si)->say_hello (); - } + /* should not be called if surfaces are not connected, but will not + * malfunction if it is. + */ - if (_device_info.no_handshake()) { - for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) { - (*si)->turn_it_on (); - } + for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) { + (*si)->connected (); } } // go to the previous track. // Assume that get_sorted_routes().size() > route_table.size() -void +void MackieControlProtocol::prev_track() { if (_current_initial_bank >= 1) { @@ -197,7 +220,7 @@ MackieControlProtocol::prev_track() // go to the next track. // Assume that get_sorted_routes().size() > route_table.size() -void +void MackieControlProtocol::next_track() { Sorted sorted = get_sorted_routes(); @@ -236,7 +259,7 @@ struct RouteByRemoteId } }; -MackieControlProtocol::Sorted +MackieControlProtocol::Sorted MackieControlProtocol::get_sorted_routes() { Sorted sorted; @@ -272,34 +295,80 @@ MackieControlProtocol::get_sorted_routes() switch (_view_mode) { case Mixer: + if (route->route_group()) { + route->route_group()->set_active (true, this); + } + 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()); + } break; case Busses: + if (Profile->get_mixbus()) { +#ifdef MIXBUS + if (route->mixbus()) { + sorted.push_back (route); + remote_ids.insert (route->remote_control_id()); + } +#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()); + } + } 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()); + } break; - case Dynamics: - break; - case EQ: - break; - case Loop: + case Plugins: break; - case Sends: + 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()); + } break; - case Plugins: + 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()); + } break; } - sorted.push_back (*it); - remote_ids.insert (route->remote_control_id()); } sort (sorted.begin(), sorted.end(), RouteByRemoteId()); return sorted; } -void +void MackieControlProtocol::refresh_current_bank() { switch_banks (_current_initial_bank, true); @@ -317,7 +386,7 @@ MackieControlProtocol::n_strips (bool with_locked_strips) const return strip_count; } -void +void 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)); @@ -336,7 +405,6 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force) */ return; } - _current_initial_bank = initial; _current_selected_track = -1; @@ -344,12 +412,14 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force) if (_current_initial_bank <= sorted.size()) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2, available routes %3\n", _current_initial_bank, strip_cnt, sorted.size())); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2, available routes %3 on %4 surfaces\n", + _current_initial_bank, strip_cnt, sorted.size(), + surfaces.size())); // link routes to strips Sorted::iterator r = sorted.begin() + _current_initial_bank; - + for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) { vector > routes; uint32_t added = 0; @@ -366,18 +436,15 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force) } } - /* reset this to get the right display of view mode after the switch */ - set_view_mode (_view_mode); - /* make sure selection is correct */ - - _gui_track_selection_changed (&_last_selected_routes, false); - + + _gui_track_selection_changed (&_last_selected_routes, false, false); + /* current bank has not been saved */ session->set_dirty(); } -int +int MackieControlProtocol::set_active (bool yn) { DEBUG_TRACE (DEBUG::MackieControl, string_compose("MackieControlProtocol::set_active init with yn: '%1'\n", yn)); @@ -387,24 +454,30 @@ MackieControlProtocol::set_active (bool yn) } if (yn) { - + /* start event loop */ - + BaseUI::run (); - - if (create_surfaces ()) { - return -1; - } + connect_session_signals (); - update_surfaces (); - + + if (!_device_info.name().empty()) { + set_device (_device_info.name(), true); + } + /* set up periodic task for metering and automation */ - + Glib::RefPtr periodic_timeout = Glib::TimeoutSource::create (100); // 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 */ + + Glib::RefPtr redisplay_timeout = Glib::TimeoutSource::create (10); // milliseconds + redisplay_connection = redisplay_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::redisplay)); + redisplay_timeout->attach (main_loop()->get_context()); + } else { BaseUI::quit (); @@ -419,6 +492,18 @@ MackieControlProtocol::set_active (bool yn) return 0; } +bool +MackieControlProtocol::hui_heartbeat () +{ + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->hui_heartbeat (); + } + + return true; +} + bool MackieControlProtocol::periodic () { @@ -430,16 +515,12 @@ MackieControlProtocol::periodic () ipmidi_restart (); return true; } - + if (!_initialized) { initialize(); } - struct timeval now; - uint64_t now_usecs; - gettimeofday (&now, 0); - - now_usecs = (now.tv_sec * 1000000) + now.tv_usec; + ARDOUR::microseconds_t now_usecs = ARDOUR::get_microseconds (); { Glib::Threads::Mutex::Lock lm (surfaces_lock); @@ -450,11 +531,40 @@ MackieControlProtocol::periodic () } update_timecode_display (); - + + return true; +} + +bool +MackieControlProtocol::redisplay () +{ + if (!active()) { + return false; + } + + if (needs_ipmidi_restart) { + ipmidi_restart (); + return true; + } + + if (!_initialized) { + initialize(); + } + + ARDOUR::microseconds_t now = ARDOUR::get_microseconds (); + + { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->redisplay (now); + } + } + return true; } -void +void MackieControlProtocol::update_timecode_beats_led() { if (!_device_info.has_timecode_display()) { @@ -478,16 +588,24 @@ MackieControlProtocol::update_timecode_beats_led() } } -void +void MackieControlProtocol::update_global_button (int id, LedState ls) { - Glib::Threads::Mutex::Lock lm (surfaces_lock); + boost::shared_ptr surface; - if (!_device_info.has_global_controls()) { - return; - } + { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + if (surfaces.empty()) { + return; + } - boost::shared_ptr surface = surfaces.front(); + if (!_device_info.has_global_controls()) { + return; + } + // surface needs to be master surface + surface = _master_surface; + } map::iterator x = surface->controls_by_device_independent_id.find (id); if (x != surface->controls_by_device_independent_id.end()) { @@ -498,16 +616,19 @@ MackieControlProtocol::update_global_button (int id, LedState ls) } } -void +void MackieControlProtocol::update_global_led (int id, LedState ls) { Glib::Threads::Mutex::Lock lm (surfaces_lock); - if (!_device_info.has_global_controls()) { + if (surfaces.empty()) { return; } - boost::shared_ptr surface = surfaces.front(); + if (!_device_info.has_global_controls()) { + return; + } + boost::shared_ptr surface = _master_surface; map::iterator x = surface->controls_by_device_independent_id.find (id); @@ -520,11 +641,24 @@ 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); +} + // send messages to surface to set controls to correct values -void +void MackieControlProtocol::update_surfaces() { - DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::update_surfaces() init\n"); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::update_surfaces() init (active=%1)\n", active())); if (!active()) { return; } @@ -532,7 +666,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); - + DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::update_surfaces() finished\n"); } @@ -541,18 +675,18 @@ MackieControlProtocol::initialize() { { Glib::Threads::Mutex::Lock lm (surfaces_lock); - + if (surfaces.empty()) { return; } - - if (!surfaces.front()->active ()) { + + if (!_master_surface->active ()) { return; } - + // sometimes the jog wheel is a pot if (_device_info.has_jog_wheel()) { - surfaces.front()->blank_jog_ring (); + _master_surface->blank_jog_ring (); } } @@ -561,15 +695,16 @@ MackieControlProtocol::initialize() notify_record_state_changed(); notify_transport_state_changed(); update_timecode_beats_led(); - + _initialized = true; } -void +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); // receive record state toggled session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_record_state_changed, this), this); // receive transport state changed @@ -593,19 +728,15 @@ MackieControlProtocol::connect_session_signals() void MackieControlProtocol::set_profile (const string& profile_name) { - if (profile_name == "default") { - /* reset to default */ - _device_profile = DeviceProfile (profile_name); - } - map::iterator d = DeviceProfile::device_profiles.find (profile_name); if (d == DeviceProfile::device_profiles.end()) { + _device_profile = DeviceProfile (profile_name); return; } - + _device_profile = d->second; -} +} int MackieControlProtocol::set_device_info (const string& device_name) @@ -617,57 +748,137 @@ MackieControlProtocol::set_device_info (const string& device_name) if (d == DeviceInfo::device_info.end()) { return -1; } - + _device_info = d->second; return 0; } int -MackieControlProtocol::set_device (const string& device_name) +MackieControlProtocol::set_device (const string& device_name, bool force) { + if (device_name == device_info().name() && !force) { + /* already using that device, nothing to do */ + return 0; + } + /* get state from the current setup, and make sure it is stored in + the configuration_states node so that if we switch back to this device, + we will have its state available. + */ + + { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + if (!surfaces.empty()) { + update_configuration_state (); + } + } + if (set_device_info (device_name)) { return -1; } clear_surfaces (); + port_connection.disconnect (); + hui_connection.disconnect (); + + if (_device_info.device_type() == DeviceInfo::HUI) { + Glib::RefPtr hui_timeout = Glib::TimeoutSource::create (1000); // milliseconds + hui_connection = hui_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::hui_heartbeat)); + hui_timeout->attach (main_loop()->get_context()); + } + + if (!_device_info.uses_ipmidi()) { + /* notice that the handler for this will execute in our event + loop, not in the thread where the + PortConnectedOrDisconnected signal is emitted. + */ + ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::connection_handler, this, _1, _2, _3, _4, _5), this); + } if (create_surfaces ()) { return -1; } - switch_banks (0, true); + DeviceChanged (); return 0; } +gboolean +ArdourSurface::ipmidi_input_handler (GIOChannel*, GIOCondition condition, void *data) +{ + ArdourSurface::MackieControlProtocol::ipMIDIHandler* ipm = static_cast(data); + return ipm->mcp->midi_input_handler (Glib::IOCondition (condition), ipm->port); +} + int MackieControlProtocol::create_surfaces () { string device_name; - surface_type_t stype = mcu; - char buf[128]; + surface_type_t stype = mcu; // type not yet determined + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces for %2\n", 1 + _device_info.extenders(), _device_info.name())); - if (_device_info.extenders() == 0) { - device_name = X_("mackie control"); + if (!_device_info.uses_ipmidi()) { + _input_bundle.reset (new ARDOUR::Bundle (_("Mackie Control In"), true)); + _output_bundle.reset (new ARDOUR::Bundle (_("Mackie Control Out"), false)); } else { - device_name = X_("mackie control #1"); + _input_bundle.reset (); + _output_bundle.reset (); + } + for (uint32_t n = 0; n < 1 + _device_info.extenders(); ++n) { + bool is_master = false; + + if (n == _device_info.master_position()) { + is_master = true; + if (_device_info.extenders() == 0) { + device_name = _device_info.name(); + } else { + device_name = X_("mackie control"); + } - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n", 1 + _device_info.extenders())); + } - for (uint32_t n = 0; n < 1 + _device_info.extenders(); ++n) { + if (!is_master) { + device_name = string_compose (X_("mackie control ext %1"), n+1); + } + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Port Name for surface %1 is %2\n", n, device_name)); boost::shared_ptr surface; + if (is_master) { + stype = mcu; + } else { + stype = ext; + } try { surface.reset (new Surface (*this, device_name, n, stype)); } catch (...) { return -1; } - if (_surfaces_state) { - surface->set_state (*_surfaces_state, _surfaces_version); + if (is_master) { + _master_surface = surface; + } + + if (configuration_state) { + 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")); + if (prop && prop->value() == _device_info.name()) { + this_device = *d; + break; + } + } + if (this_device) { + XMLNode* snode = this_device->child (X_("Surfaces")); + if (snode) { + surface->set_state (*snode, state_version); + } + } } { @@ -675,66 +886,84 @@ MackieControlProtocol::create_surfaces () surfaces.push_back (surface); } - /* next device will be an extender */ - - if (_device_info.extenders() < 2) { - device_name = X_("mackie control #2"); - } else { - snprintf (buf, sizeof (buf), X_("mackie control #%d"), n+2); - device_name = buf; - } - stype = ext; - if (!_device_info.uses_ipmidi()) { - _input_bundle.reset (new ARDOUR::Bundle (_("Mackie Control In"), true)); - _output_bundle.reset (new ARDOUR::Bundle (_("Mackie Control Out"), false)); - _input_bundle->add_channel ( surface->port().input_port().name(), ARDOUR::DataType::MIDI, session->engine().make_port_name_non_relative (surface->port().input_port().name()) ); - + _output_bundle->add_channel ( surface->port().output_port().name(), ARDOUR::DataType::MIDI, session->engine().make_port_name_non_relative (surface->port().output_port().name()) ); + } + + MIDI::Port& input_port (surface->port().input_port()); + AsyncMIDIPort* asp = dynamic_cast (&input_port); + + if (asp) { - session->BundleAdded (_input_bundle); - session->BundleAdded (_output_bundle); + /* async MIDI port */ + + asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &MackieControlProtocol::midi_input_handler), &input_port)); + asp->xthread().attach (main_loop()->get_context()); } else { - _input_bundle.reset ((ARDOUR::Bundle*) 0); - _output_bundle.reset ((ARDOUR::Bundle*) 0); - session->BundleRemoved (_input_bundle); - session->BundleRemoved (_output_bundle); - } + /* ipMIDI port, no IOSource method at this time */ - int fd; - MIDI::Port& input_port (surface->port().input_port()); + int fd; + + if ((fd = input_port.selectable ()) >= 0) { + + GIOChannel* ioc = g_io_channel_unix_new (fd); + surface->input_source = g_io_create_watch (ioc, GIOCondition (G_IO_IN|G_IO_HUP|G_IO_ERR)); + + /* make surface's input source now hold the + * only reference on the IO channel + */ + g_io_channel_unref (ioc); - if ((fd = input_port.selectable ()) >= 0) { - Glib::RefPtr psrc = IOSource::create (fd, IO_IN|IO_HUP|IO_ERR); + /* hack up an object so that in the callback from the event loop + we have both the MackieControlProtocol and the input port. - psrc->connect (sigc::bind (sigc::mem_fun (this, &MackieControlProtocol::midi_input_handler), &input_port)); - psrc->attach (main_loop()->get_context()); - - // glibmm hack: for now, store only the GSource* + If we were using C++ for this stuff we wouldn't need this + but a nasty, not-fixable bug in the binding between C + and C++ makes it necessary to avoid C++ for the IO + callback setup. + */ - port_sources.push_back (psrc->gobj()); - g_source_ref (psrc->gobj()); + ipMIDIHandler* ipm = new ipMIDIHandler (); /* we will leak this sizeof(pointer)*2 sized object */ + ipm->mcp = this; + ipm->port = &input_port; + + g_source_set_callback (surface->input_source, (GSourceFunc) ipmidi_input_handler, ipm, NULL); + g_source_attach (surface->input_source, main_loop()->get_context()->gobj()); + } + } + } + + if (!_device_info.uses_ipmidi()) { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->port().reconnect (); } } + session->BundleAddedOrRemoved (); + + assert (_master_surface); + return 0; } -void +void MackieControlProtocol::close() { + port_connection.disconnect (); session_connections.drop_connections (); route_connections.drop_connections (); periodic_connection.disconnect (); @@ -742,7 +971,35 @@ MackieControlProtocol::close() clear_surfaces(); } -XMLNode& +/** Ensure that the configuration_state XML node contains an up-to-date + * copy of the state node the current device. If configuration_state already + * contains a state node for the device, it will deleted and replaced. + */ +void +MackieControlProtocol::update_configuration_state () +{ + /* CALLER MUST HOLD SURFACES LOCK */ + + if (!configuration_state) { + configuration_state = new XMLNode (X_("Configurations")); + } + + XMLNode* devnode = new XMLNode (X_("Configuration")); + devnode->add_property (X_("name"), _device_info.name()); + + configuration_state->remove_nodes_and_delete (X_("name"), _device_info.name()); + configuration_state->add_child_nocopy (*devnode); + + XMLNode* snode = new XMLNode (X_("Surfaces")); + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + snode->add_child_nocopy ((*s)->get_state()); + } + + devnode->add_child_nocopy (*snode); +} + +XMLNode& MackieControlProtocol::get_state() { XMLNode& node (ControlProtocol::get_state()); @@ -761,19 +1018,20 @@ MackieControlProtocol::get_state() node.add_property (X_("device-profile"), _device_profile.name()); node.add_property (X_("device-name"), _device_info.name()); - XMLNode* snode = new XMLNode (X_("Surfaces")); - for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - snode->add_child_nocopy ((*s)->get_state()); + { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + update_configuration_state (); } - node.add_child_nocopy (*snode); + /* force a copy of the _surfaces_state node, because we want to retain ownership */ + node.add_child_copy (*configuration_state); DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::get_state done\n"); return node; } -int +int MackieControlProtocol::set_state (const XMLNode & node, int version) { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::set_state: active %1\n", active())); @@ -782,6 +1040,10 @@ MackieControlProtocol::set_state (const XMLNode & node, int version) 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())); } @@ -790,33 +1052,43 @@ MackieControlProtocol::set_state (const XMLNode & node, int version) if ((prop = node.property (X_("bank"))) != 0) { bank = atoi (prop->value()); } - + if ((prop = node.property (X_("device-name"))) != 0) { set_device_info (prop->value()); } if ((prop = node.property (X_("device-profile"))) != 0) { - set_profile (prop->value()); + if (prop->value().empty()) { + string default_profile_name; + + default_profile_name = Glib::get_user_name(); + default_profile_name += ' '; + default_profile_name += _device_info.name(); + + set_profile (default_profile_name); + } else { + set_profile (prop->value()); + } } - - XMLNode* snode = node.child (X_("Surfaces")); - - delete _surfaces_state; - _surfaces_state = 0; - if (snode) { - _surfaces_state = new XMLNode (*snode); - _surfaces_version = version; + XMLNode* dnode = node.child (X_("Configurations")); + + delete configuration_state; + configuration_state = 0; + + if (dnode) { + configuration_state = new XMLNode (*dnode); + state_version = version; } switch_banks (bank, true); - + DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::set_state done\n"); return retval; } -string +string MackieControlProtocol::format_bbt_timecode (framepos_t now_frame) { Timecode::BBT_Time bbt_time; @@ -843,7 +1115,7 @@ MackieControlProtocol::format_bbt_timecode (framepos_t now_frame) return os.str(); } -string +string MackieControlProtocol::format_timecode_timecode (framepos_t now_frame) { Timecode::Time timecode; @@ -863,7 +1135,7 @@ MackieControlProtocol::format_timecode_timecode (framepos_t now_frame) return os.str(); } -void +void MackieControlProtocol::update_timecode_display() { Glib::Threads::Mutex::Lock lm (surfaces_lock); @@ -872,7 +1144,7 @@ MackieControlProtocol::update_timecode_display() return; } - boost::shared_ptr surface = surfaces.front(); + boost::shared_ptr surface = _master_surface; if (surface->type() != mcu || !_device_info.has_timecode_display() || !surface->active ()) { return; @@ -881,6 +1153,13 @@ MackieControlProtocol::update_timecode_display() // do assignment here so current_frame is fixed framepos_t current_frame = session->transport_frame(); string timecode; + // For large jumps in play head possition do full reset + int moved = (current_frame - _frame_last) / session->frame_rate (); + if (moved) { + DEBUG_TRACE (DEBUG::MackieControl, "Timecode reset\n"); + _timecode_last = string (10, ' '); + } + _frame_last = current_frame; switch (_timecode_type) { case ARDOUR::AnyTime::BBT: @@ -892,7 +1171,7 @@ MackieControlProtocol::update_timecode_display() default: return; } - + // only write the timecode string to the MCU if it's changed // since last time. This is to reduce midi bandwidth used. if (timecode != _timecode_last) { @@ -908,20 +1187,48 @@ MackieControlProtocol::update_timecode_display() void MackieControlProtocol::notify_parameter_changed (std::string const & p) { if (p == "punch-in") { - update_global_button (Button::PunchIn, session->config.get_punch_in()); + // no such button right now + // update_global_button (Button::PunchIn, session->config.get_punch_in()); } else if (p == "punch-out") { - update_global_button (Button::PunchOut, session->config.get_punch_out()); + // no such button right now + // update_global_button (Button::PunchOut, session->config.get_punch_out()); } else if (p == "clicking") { - // update_global_button (Button::RelayClick, Config->get_clicking()); + update_global_button (Button::Click, Config->get_clicking()); } else { DEBUG_TRACE (DEBUG::MackieControl, string_compose ("parameter changed: %1\n", p)); } } +void +MackieControlProtocol::notify_route_added_or_removed () +{ + Glib::Threads::Mutex::Lock lm (surfaces_lock); + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->master_monitor_may_have_changed (); + } +} + // RouteList is the set of routes that have just been added -void +void MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl) { + { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + if (surfaces.empty()) { + return; + } + } + + /* special case: single route, and it is the monitor or master out */ + + if (rl.size() == 1 && (rl.front()->is_monitor() || rl.front()->is_master())) { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->master_monitor_may_have_changed (); + } + } + // currently assigned banks are less than the full set of // strips, so activate the new strip now. @@ -937,28 +1244,41 @@ MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl) } } -void +void MackieControlProtocol::notify_solo_active_changed (bool active) { boost::shared_ptr surface; { Glib::Threads::Mutex::Lock lm (surfaces_lock); - surface = surfaces.front (); - } - - map::iterator x = surface->controls_by_device_independent_id.find (Led::RudeSolo); - if (x != surface->controls_by_device_independent_id.end()) { - Led* rude_solo = dynamic_cast (x->second); + + if (surfaces.empty()) { + return; + } + + surface = _master_surface; + } + + map::iterator x = surface->controls_by_device_independent_id.find (Led::RudeSolo); + if (x != surface->controls_by_device_independent_id.end()) { + Led* rude_solo = dynamic_cast (x->second); if (rude_solo) { surface->write (rude_solo->set_state (active ? flashing : off)); } } } -void +void MackieControlProtocol::notify_remote_id_changed() { + { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + if (surfaces.empty()) { + return; + } + } + Sorted sorted = get_sorted_routes(); uint32_t sz = n_strips(); @@ -978,13 +1298,13 @@ MackieControlProtocol::notify_remote_id_changed() // Transport signals /////////////////////////////////////////// -void +void MackieControlProtocol::notify_loop_state_changed() { update_global_button (Button::Loop, session->get_play_loop()); } -void +void MackieControlProtocol::notify_transport_state_changed() { if (!_device_info.has_global_controls()) { @@ -998,17 +1318,20 @@ MackieControlProtocol::notify_transport_state_changed() update_global_button (Button::Rewind, session->transport_speed() < 0.0); update_global_button (Button::Ffwd, session->transport_speed() > 1.0); + // sometimes a return to start leaves time code at old time + _timecode_last = string (10, ' '); + notify_metering_state_changed (); } -void +void MackieControlProtocol::notify_metering_state_changed() { Glib::Threads::Mutex::Lock lm (surfaces_lock); for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { (*s)->notify_metering_state_changed (); - } + } } void @@ -1022,9 +1345,12 @@ MackieControlProtocol::notify_record_state_changed () { Glib::Threads::Mutex::Lock lm (surfaces_lock); - surface = surfaces.front(); + if (surfaces.empty()) { + return; + } + surface = _master_surface; } - + /* rec is a tristate */ map::iterator x = surface->controls_by_device_independent_id.find (Button::Record); @@ -1032,7 +1358,7 @@ MackieControlProtocol::notify_record_state_changed () Button * rec = dynamic_cast (x->second); if (rec) { LedState ls; - + switch (session->record_status()) { case Session::Disabled: DEBUG_TRACE (DEBUG::MackieControl, "record state changed to disabled, LED off\n"); @@ -1087,7 +1413,7 @@ MackieControlProtocol::stop () return 0; } -void +void MackieControlProtocol::update_led (Surface& surface, Button& button, Mackie::LedState ls) { if (ls != none) { @@ -1103,8 +1429,8 @@ MackieControlProtocol::build_button_map () #define DEFINE_BUTTON_HANDLER(b,p,r) button_map.insert (pair ((b), ButtonHandlers ((p),(r)))); - DEFINE_BUTTON_HANDLER (Button::IO, &MackieControlProtocol::io_press, &MackieControlProtocol::io_release); - DEFINE_BUTTON_HANDLER (Button::Sends, &MackieControlProtocol::sends_press, &MackieControlProtocol::sends_release); + DEFINE_BUTTON_HANDLER (Button::Track, &MackieControlProtocol::track_press, &MackieControlProtocol::track_release); + DEFINE_BUTTON_HANDLER (Button::Send, &MackieControlProtocol::send_press, &MackieControlProtocol::send_release); DEFINE_BUTTON_HANDLER (Button::Pan, &MackieControlProtocol::pan_press, &MackieControlProtocol::pan_release); DEFINE_BUTTON_HANDLER (Button::Plugin, &MackieControlProtocol::plugin_press, &MackieControlProtocol::plugin_release); DEFINE_BUTTON_HANDLER (Button::Eq, &MackieControlProtocol::eq_press, &MackieControlProtocol::eq_release); @@ -1114,7 +1440,7 @@ MackieControlProtocol::build_button_map () DEFINE_BUTTON_HANDLER (Button::ChannelLeft, &MackieControlProtocol::channel_left_press, &MackieControlProtocol::channel_left_release); DEFINE_BUTTON_HANDLER (Button::ChannelRight, &MackieControlProtocol::channel_right_press, &MackieControlProtocol::channel_right_release); DEFINE_BUTTON_HANDLER (Button::Flip, &MackieControlProtocol::flip_press, &MackieControlProtocol::flip_release); - DEFINE_BUTTON_HANDLER (Button::Edit, &MackieControlProtocol::edit_press, &MackieControlProtocol::edit_release); + 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); @@ -1125,35 +1451,35 @@ MackieControlProtocol::build_button_map () 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::F9, &MackieControlProtocol::F9_press, &MackieControlProtocol::F9_release); - DEFINE_BUTTON_HANDLER (Button::F10, &MackieControlProtocol::F10_press, &MackieControlProtocol::F10_release); - DEFINE_BUTTON_HANDLER (Button::F11, &MackieControlProtocol::F11_press, &MackieControlProtocol::F11_release); - DEFINE_BUTTON_HANDLER (Button::F12, &MackieControlProtocol::F12_press, &MackieControlProtocol::F12_release); - DEFINE_BUTTON_HANDLER (Button::F13, &MackieControlProtocol::F13_press, &MackieControlProtocol::F13_release); - DEFINE_BUTTON_HANDLER (Button::F14, &MackieControlProtocol::F14_press, &MackieControlProtocol::F14_release); - DEFINE_BUTTON_HANDLER (Button::F15, &MackieControlProtocol::F15_press, &MackieControlProtocol::F15_release); - DEFINE_BUTTON_HANDLER (Button::F16, &MackieControlProtocol::F16_press, &MackieControlProtocol::F16_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); + DEFINE_BUTTON_HANDLER (Button::AudioInstruments, &MackieControlProtocol::audioinstruments_press, &MackieControlProtocol::audioinstruments_release); + DEFINE_BUTTON_HANDLER (Button::Aux, &MackieControlProtocol::aux_press, &MackieControlProtocol::aux_release); + DEFINE_BUTTON_HANDLER (Button::Busses, &MackieControlProtocol::busses_press, &MackieControlProtocol::busses_release); + DEFINE_BUTTON_HANDLER (Button::Outputs, &MackieControlProtocol::outputs_press, &MackieControlProtocol::outputs_release); + DEFINE_BUTTON_HANDLER (Button::User, &MackieControlProtocol::user_press, &MackieControlProtocol::user_release); DEFINE_BUTTON_HANDLER (Button::Shift, &MackieControlProtocol::shift_press, &MackieControlProtocol::shift_release); DEFINE_BUTTON_HANDLER (Button::Option, &MackieControlProtocol::option_press, &MackieControlProtocol::option_release); DEFINE_BUTTON_HANDLER (Button::Ctrl, &MackieControlProtocol::control_press, &MackieControlProtocol::control_release); DEFINE_BUTTON_HANDLER (Button::CmdAlt, &MackieControlProtocol::cmd_alt_press, &MackieControlProtocol::cmd_alt_release); - DEFINE_BUTTON_HANDLER (Button::On, &MackieControlProtocol::on_press, &MackieControlProtocol::on_release); - DEFINE_BUTTON_HANDLER (Button::RecReady, &MackieControlProtocol::rec_ready_press, &MackieControlProtocol::rec_ready_release); - DEFINE_BUTTON_HANDLER (Button::Undo, &MackieControlProtocol::undo_press, &MackieControlProtocol::undo_release); - DEFINE_BUTTON_HANDLER (Button::Save, &MackieControlProtocol::save_press, &MackieControlProtocol::save_release); + DEFINE_BUTTON_HANDLER (Button::Read, &MackieControlProtocol::read_press, &MackieControlProtocol::read_release); + DEFINE_BUTTON_HANDLER (Button::Write, &MackieControlProtocol::write_press, &MackieControlProtocol::write_release); + DEFINE_BUTTON_HANDLER (Button::Trim, &MackieControlProtocol::trim_press, &MackieControlProtocol::trim_release); DEFINE_BUTTON_HANDLER (Button::Touch, &MackieControlProtocol::touch_press, &MackieControlProtocol::touch_release); - DEFINE_BUTTON_HANDLER (Button::Redo, &MackieControlProtocol::redo_press, &MackieControlProtocol::redo_release); - DEFINE_BUTTON_HANDLER (Button::Marker, &MackieControlProtocol::marker_press, &MackieControlProtocol::marker_release); - DEFINE_BUTTON_HANDLER (Button::Enter, &MackieControlProtocol::enter_press, &MackieControlProtocol::enter_release); + DEFINE_BUTTON_HANDLER (Button::Latch, &MackieControlProtocol::latch_press, &MackieControlProtocol::latch_release); + DEFINE_BUTTON_HANDLER (Button::Grp, &MackieControlProtocol::grp_press, &MackieControlProtocol::grp_release); + DEFINE_BUTTON_HANDLER (Button::Save, &MackieControlProtocol::save_press, &MackieControlProtocol::save_release); + DEFINE_BUTTON_HANDLER (Button::Undo, &MackieControlProtocol::undo_press, &MackieControlProtocol::undo_release); DEFINE_BUTTON_HANDLER (Button::Cancel, &MackieControlProtocol::cancel_press, &MackieControlProtocol::cancel_release); - DEFINE_BUTTON_HANDLER (Button::Mixer, &MackieControlProtocol::mixer_press, &MackieControlProtocol::mixer_release); - DEFINE_BUTTON_HANDLER (Button::FrmLeft, &MackieControlProtocol::frm_left_press, &MackieControlProtocol::frm_left_release); - DEFINE_BUTTON_HANDLER (Button::FrmRight, &MackieControlProtocol::frm_right_press, &MackieControlProtocol::frm_right_release); + DEFINE_BUTTON_HANDLER (Button::Enter, &MackieControlProtocol::enter_press, &MackieControlProtocol::enter_release); + DEFINE_BUTTON_HANDLER (Button::Marker, &MackieControlProtocol::marker_press, &MackieControlProtocol::marker_release); + DEFINE_BUTTON_HANDLER (Button::Nudge, &MackieControlProtocol::nudge_press, &MackieControlProtocol::nudge_release); DEFINE_BUTTON_HANDLER (Button::Loop, &MackieControlProtocol::loop_press, &MackieControlProtocol::loop_release); - DEFINE_BUTTON_HANDLER (Button::PunchIn, &MackieControlProtocol::punch_in_press, &MackieControlProtocol::punch_in_release); - DEFINE_BUTTON_HANDLER (Button::PunchOut, &MackieControlProtocol::punch_out_press, &MackieControlProtocol::punch_out_release); - DEFINE_BUTTON_HANDLER (Button::Home, &MackieControlProtocol::home_press, &MackieControlProtocol::home_release); - DEFINE_BUTTON_HANDLER (Button::End, &MackieControlProtocol::end_press, &MackieControlProtocol::end_release); + DEFINE_BUTTON_HANDLER (Button::Drop, &MackieControlProtocol::drop_press, &MackieControlProtocol::drop_release); + DEFINE_BUTTON_HANDLER (Button::Replace, &MackieControlProtocol::replace_press, &MackieControlProtocol::replace_release); + DEFINE_BUTTON_HANDLER (Button::Click, &MackieControlProtocol::click_press, &MackieControlProtocol::click_release); + DEFINE_BUTTON_HANDLER (Button::ClearSolo, &MackieControlProtocol::clearsolo_press, &MackieControlProtocol::clearsolo_release); DEFINE_BUTTON_HANDLER (Button::Rewind, &MackieControlProtocol::rewind_press, &MackieControlProtocol::rewind_release); DEFINE_BUTTON_HANDLER (Button::Ffwd, &MackieControlProtocol::ffwd_press, &MackieControlProtocol::ffwd_release); DEFINE_BUTTON_HANDLER (Button::Stop, &MackieControlProtocol::stop_press, &MackieControlProtocol::stop_release); @@ -1168,85 +1494,87 @@ MackieControlProtocol::build_button_map () DEFINE_BUTTON_HANDLER (Button::UserA, &MackieControlProtocol::user_a_press, &MackieControlProtocol::user_a_release); DEFINE_BUTTON_HANDLER (Button::UserB, &MackieControlProtocol::user_b_press, &MackieControlProtocol::user_b_release); DEFINE_BUTTON_HANDLER (Button::MasterFaderTouch, &MackieControlProtocol::master_fader_touch_press, &MackieControlProtocol::master_fader_touch_release); - - DEFINE_BUTTON_HANDLER (Button::Snapshot, &MackieControlProtocol::snapshot_press, &MackieControlProtocol::snapshot_release); - DEFINE_BUTTON_HANDLER (Button::Read, &MackieControlProtocol::read_press, &MackieControlProtocol::read_release); - DEFINE_BUTTON_HANDLER (Button::Write, &MackieControlProtocol::write_press, &MackieControlProtocol::write_release); - DEFINE_BUTTON_HANDLER (Button::FdrGroup, &MackieControlProtocol::fdrgroup_press, &MackieControlProtocol::fdrgroup_release); - DEFINE_BUTTON_HANDLER (Button::ClearSolo, &MackieControlProtocol::clearsolo_press, &MackieControlProtocol::clearsolo_release); - DEFINE_BUTTON_HANDLER (Button::Track, &MackieControlProtocol::track_press, &MackieControlProtocol::track_release); - DEFINE_BUTTON_HANDLER (Button::Send, &MackieControlProtocol::send_press, &MackieControlProtocol::send_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); - DEFINE_BUTTON_HANDLER (Button::AudioInstruments, &MackieControlProtocol::audioinstruments_press, &MackieControlProtocol::audioinstruments_release); - DEFINE_BUTTON_HANDLER (Button::Aux, &MackieControlProtocol::aux_press, &MackieControlProtocol::aux_release); - DEFINE_BUTTON_HANDLER (Button::Busses, &MackieControlProtocol::busses_press, &MackieControlProtocol::busses_release); - DEFINE_BUTTON_HANDLER (Button::Outputs, &MackieControlProtocol::outputs_press, &MackieControlProtocol::outputs_release); - DEFINE_BUTTON_HANDLER (Button::User, &MackieControlProtocol::user_press, &MackieControlProtocol::user_release); - DEFINE_BUTTON_HANDLER (Button::Trim, &MackieControlProtocol::trim_press, &MackieControlProtocol::trim_release); - DEFINE_BUTTON_HANDLER (Button::Latch, &MackieControlProtocol::latch_press, &MackieControlProtocol::latch_release); - DEFINE_BUTTON_HANDLER (Button::Grp, &MackieControlProtocol::grp_press, &MackieControlProtocol::grp_release); - DEFINE_BUTTON_HANDLER (Button::Nudge, &MackieControlProtocol::nudge_press, &MackieControlProtocol::nudge_release); - DEFINE_BUTTON_HANDLER (Button::Drop, &MackieControlProtocol::drop_press, &MackieControlProtocol::drop_release); - DEFINE_BUTTON_HANDLER (Button::Replace, &MackieControlProtocol::replace_press, &MackieControlProtocol::replace_release); - DEFINE_BUTTON_HANDLER (Button::Click, &MackieControlProtocol::click_press, &MackieControlProtocol::click_release); - DEFINE_BUTTON_HANDLER (Button::View, &MackieControlProtocol::view_press, &MackieControlProtocol::view_release); } -void +void MackieControlProtocol::handle_button_event (Surface& surface, Button& button, ButtonState bs) { + Button::ID button_id = button.bid(); + if (bs != press && bs != release) { update_led (surface, button, none); return; } - + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Handling %1 for button %2 (%3)\n", (bs == press ? "press" : "release"), button.id(), Button::id_to_name (button.bid()))); /* check profile first */ - + string action = _device_profile.get_button_action (button.bid(), _modifier_state); - - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Looked up action for button %1 with modifier %2, got [%3]\n", - button.bid(), _modifier_state, action)); if (!action.empty()) { - /* if there is a bound action for this button, and this is a press event, - carry out the action. If its a release event, do nothing since we - don't bind to them at all but don't want any other handling to - occur either. - */ - if (bs == press) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("executing action %1\n", action)); - access_action (action); + + if (action.find ('/') != string::npos) { /* good chance that this is really an action */ + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Looked up action for button %1 with modifier %2, got [%3]\n", + button.bid(), _modifier_state, action)); + + /* if there is a bound action for this button, and this is a press event, + carry out the action. If its a release event, do nothing since we + don't bind to them at all but don't want any other handling to + occur either. + */ + if (bs == press) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("executing action %1\n", action)); + access_action (action); + } + + return; + + } else { + + /* "action" is more likely to be a button name. We use this to + * allow remapping buttons to different (builtin) functionality + * associated with an existing button. This is similar to the + * way that (for example) Nuendo moves the "Shift" function to + * the "Enter" key of the MCU Pro. + */ + + int bid = Button::name_to_id (action); + + if (bid < 0) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("apparent button name %1 not found\n", action)); + return; + } + + button_id = (Button::ID) bid; + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handling button %1 as if it was %2 (%3)\n", Button::id_to_name (button.bid()), button_id, Button::id_to_name (button_id))); } - return; } /* lookup using the device-INDEPENDENT button ID */ - ButtonMap::iterator b = button_map.find (button.bid()); + ButtonMap::iterator b = button_map.find (button_id); if (b != button_map.end()) { ButtonHandlers& bh (b->second); switch (bs) { - case press: + case press: surface.write (button.set_state ((this->*(bh.press)) (button))); break; - case release: + case release: surface.write (button.set_state ((this->*(bh.release)) (button))); break; default: break; } } else { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button handlers for button ID %1 (device ID %2)\n", + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button handlers for button ID %1 (device ID %2)\n", button.bid(), button.id())); - error << string_compose ("no button handlers for button ID %1 (device ID %2)\n", + error << string_compose ("no button handlers for button ID %1 (device ID %2)\n", button.bid(), button.id()) << endmsg; } } @@ -1254,14 +1582,15 @@ MackieControlProtocol::handle_button_event (Surface& surface, Button& button, Bu bool MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port) { - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("something happend on %1\n", port->name())); - if (ioc & ~IO_IN) { + DEBUG_TRACE (DEBUG::MackieControl, "MIDI port closed\n"); return false; } if (ioc & IO_IN) { + 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. @@ -1271,9 +1600,10 @@ MackieControlProtocol::midi_input_handler (IOCondition ioc, MIDI::Port* port) */ if (!_device_info.uses_ipmidi()) { -#ifndef PLATFORM_WINDOWS - CrossThreadChannel::drain (port->selectable()); -#endif + AsyncMIDIPort* asp = dynamic_cast(port); + if (asp) { + asp->clear (); + } } DEBUG_TRACE (DEBUG::MackieControl, string_compose ("data available on %1\n", port->name())); @@ -1291,40 +1621,173 @@ MackieControlProtocol::clear_ports () _input_bundle->remove_channels (); _output_bundle->remove_channels (); } +} + +void +MackieControlProtocol::notify_subview_route_deleted () +{ + /* return to global/mixer view */ + _subview_route.reset (); + set_view_mode (Mixer); +} - for (PortSources::iterator i = port_sources.begin(); i != port_sources.end(); ++i) { - g_source_destroy (*i); - g_source_unref (*i); +void +MackieControlProtocol::set_subview_mode (SubViewMode sm, 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; } - port_sources.clear (); + if ((_subview_mode != old_mode) || (_subview_route != old_route)) { + + 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); + } + } + + /* 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 (); + } + } + + if (_subview_mode != old_mode) { + + /* turn buttons related to vpot mode on or off as required */ + + 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; + } + } + } } void MackieControlProtocol::set_view_mode (ViewMode m) { - Glib::Threads::Mutex::Lock lm (surfaces_lock); + _last_bank[_view_mode] = _current_initial_bank; _view_mode = m; + set_subview_mode (None, boost::shared_ptr()); - for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - (*s)->update_view_mode_display (); + switch_banks(_last_bank[_view_mode], true); + 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 (); + } } - } void MackieControlProtocol::set_flip_mode (FlipMode fm) { - Glib::Threads::Mutex::Lock lm (surfaces_lock); + if (_flip_mode != fm) { + if (fm == Normal) { + update_global_button (Button::Flip, off); + } else { + update_global_button (Button::Flip, on); + } - _flip_mode = fm; - - for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - (*s)->update_flip_mode_display (); + 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_pot_mode (PotMode m) +{ + // maybe not in flip mode. + if (flip_mode()) { + return; + } + + _pot_mode = m; + + { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->update_potmode (); + + } } + + pot_mode_globals (); +} + +void +MackieControlProtocol::pot_mode_globals () +{ + update_global_button (Button::Eq, off); + update_global_button (Button::Dyn, off); + update_global_button (Button::AudioInstruments, off); + + switch (_pot_mode) { + case Trim: + update_global_button (Button::Track, on); + update_global_button (Button::Send, off); + update_global_button (Button::Pan, off); + break; + case Send: + update_global_button (Button::Track, off); + update_global_button (Button::Send, on); + update_global_button (Button::Pan, off); + break; + case Pan: + update_global_button (Button::Track, off); + update_global_button (Button::Send, off); + update_global_button (Button::Pan, on); + }; } - + void MackieControlProtocol::set_master_on_surface_strip (uint32_t surface, uint32_t strip_number) { @@ -1360,11 +1823,11 @@ MackieControlProtocol::force_special_route_to_strip (boost::shared_ptr r, void MackieControlProtocol::gui_track_selection_changed (ARDOUR::RouteNotificationListPtr rl, bool save_list) { - _gui_track_selection_changed (rl.get(), save_list); + _gui_track_selection_changed (rl.get(), save_list, true); } void -MackieControlProtocol::_gui_track_selection_changed (ARDOUR::RouteNotificationList* rl, bool save_list) +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 @@ -1386,15 +1849,20 @@ MackieControlProtocol::_gui_track_selection_changed (ARDOUR::RouteNotificationLi { Glib::Threads::Mutex::Lock lm (surfaces_lock); - + for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) { (*s)->gui_selection_changed (srl); } } - + if (save_list) { _last_selected_routes = *rl; } + + if (gui_selection_did_change) { + /* actual GUI selection changed */ + set_subview_mode (_subview_mode, first_selected_route()); + } } framepos_t @@ -1434,7 +1902,7 @@ MackieControlProtocol::select_range () if (!routes.empty()) { for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { - if (_modifier_state == MODIFIER_CONTROL) { + if (main_modifier_state() == MODIFIER_CONTROL) { ToggleRouteSelection ((*r)->remote_control_id ()); } else { if (r == routes.begin()) { @@ -1492,12 +1960,12 @@ MackieControlProtocol::down_controls (AutomationType p) if (m == _down_buttons.end()) { return controls; } - + 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); - + switch (p) { case GainAutomation: for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) { @@ -1529,7 +1997,7 @@ MackieControlProtocol::down_controls (AutomationType p) return controls; } - + struct ButtonRangeSorter { bool operator() (const uint32_t& a, const uint32_t& b) { return (a>>8) < (b>>8) // a.surface < b.surface @@ -1553,7 +2021,7 @@ MackieControlProtocol::pull_route_range (DownButtonList& down, RouteList& select uint32_t first = ldown.front(); uint32_t last = ldown.back (); - + uint32_t first_surface = first>>8; uint32_t first_strip = first&0xf; @@ -1562,11 +2030,11 @@ MackieControlProtocol::pull_route_range (DownButtonList& down, RouteList& select DEBUG_TRACE (DEBUG::MackieControl, string_compose ("PRR %5 in list %1.%2 - %3.%4\n", first_surface, first_strip, last_surface, last_strip, down.size())); - + Glib::Threads::Mutex::Lock lm (surfaces_lock); for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - + if ((*s)->number() >= first_surface && (*s)->number() <= last_surface) { uint32_t fs; @@ -1632,6 +2100,165 @@ void MackieControlProtocol::clear_surfaces () { clear_ports (); + + { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + _master_surface.reset (); + surfaces.clear (); + } +} + +void +MackieControlProtocol::set_touch_sensitivity (int sensitivity) +{ + sensitivity = min (9, sensitivity); + sensitivity = max (0, sensitivity); + + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->set_touch_sensitivity (sensitivity); + } +} + +void +MackieControlProtocol::recalibrate_faders () +{ + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->recalibrate_faders (); + } +} + +void +MackieControlProtocol::toggle_backlight () +{ Glib::Threads::Mutex::Lock lm (surfaces_lock); - surfaces.clear (); + + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + (*s)->toggle_backlight (); + } +} + +boost::shared_ptr +MackieControlProtocol::get_surface_by_raw_pointer (void* ptr) const +{ + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + if ((*s).get() == (Surface*) ptr) { + return *s; + } + } + + return boost::shared_ptr (); +} + +boost::shared_ptr +MackieControlProtocol::nth_surface (uint32_t n) const +{ + Glib::Threads::Mutex::Lock lm (surfaces_lock); + + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s, --n) { + if (n == 0) { + return *s; + } + } + + return boost::shared_ptr (); +} + +void +MackieControlProtocol::connection_handler (boost::weak_ptr wp1, std::string name1, boost::weak_ptr wp2, std::string name2, bool yn) +{ + Surfaces scopy; + { + Glib::Threads::Mutex::Lock lm (surfaces_lock); + scopy = surfaces; + } + + for (Surfaces::const_iterator s = scopy.begin(); s != scopy.end(); ++s) { + if ((*s)->connection_handler (wp1, name1, wp2, name2, yn)) { + ConnectionChange (*s); + break; + } + } +} + +bool +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 +{ + return boost::dynamic_pointer_cast(r) != 0; +} + +bool +MackieControlProtocol::is_midi_track (boost::shared_ptr r) const +{ + return boost::dynamic_pointer_cast(r) != 0; +} + +bool +MackieControlProtocol::selected (boost::shared_ptr r) const +{ + const RouteNotificationList* rl = &_last_selected_routes; + + for (ARDOUR::RouteNotificationList::const_iterator i = rl->begin(); i != rl->end(); ++i) { + boost::shared_ptr rt = (*i).lock(); + if (rt == r) { + return true; + } + } + return false; +} + +boost::shared_ptr +MackieControlProtocol::first_selected_route () const +{ + if (_last_selected_routes.empty()) { + return boost::shared_ptr(); + } + + boost::shared_ptr r = _last_selected_routes.front().lock(); + + return r; /* may be null */ +} + +boost::shared_ptr +MackieControlProtocol::subview_route () const +{ + return _subview_route; +} + +uint32_t +MackieControlProtocol::global_index (Strip& strip) +{ + Glib::Threads::Mutex::Lock lm (surfaces_lock); + uint32_t global = 0; + + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + if ((*s).get() == strip.surface()) { + return global + strip.index(); + } + global += (*s)->n_strips (); + } + + 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); }