#include <errno.h>
#include <boost/shared_array.hpp>
+#include <glibmm/miscutils.h>
#include "midi++/types.h"
#include "midi++/port.h"
#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/record_enable_control.h"
#include "ardour/route.h"
+#include "ardour/route_group.h"
#include "ardour/session.h"
#include "ardour/tempo.h"
#include "ardour/track.h"
#include "ardour/types.h"
#include "ardour/audioengine.h"
+#include "ardour/vca_manager.h"
#include "mackie_control_protocol.h"
using namespace ArdourSurface;
using namespace Mackie;
-#include "i18n.h"
+#include "pbd/i18n.h"
#include "pbd/abstract_ui.cc" // instantiate template
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|
MackieControlProtocol::MackieControlProtocol (Session& session)
: ControlProtocol (session, X_("Mackie"))
- , AbstractUI<MackieControlUIRequest> ("mackie")
+ , AbstractUI<MackieControlUIRequest> (name())
, _current_initial_bank (0)
+ , _frame_last (0)
, _timecode_type (ARDOUR::AnyTime::BBT)
, _gui (0)
, _scrub_mode (false)
, _flip_mode (Normal)
, _view_mode (Mixer)
- , _pot_mode (Pan)
+ , _subview_mode (None)
, _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)
- , _session_load (true)
+ , 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");
DeviceInfo::reload_device_info ();
DeviceProfile::reload_device_profiles ();
- TrackSelectionChanged.connect (gui_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::gui_track_selection_changed, this, _1, true), this);
+ for (int i = 0; i < 9; i++) {
+ _last_bank[i] = 0;
+ }
+
+ PresentationInfo::Change.connect (gui_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_presentation_info_changed, this), this);
_instance = this;
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");
{
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 */
}
// go to the previous track.
-// Assume that get_sorted_routes().size() > route_table.size()
void
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();
+ Sorted sorted = get_sorted_stripables();
if (_current_initial_bank + n_strips() < sorted.size()) {
switch_banks (_current_initial_bank + 1);
}
}
bool
-MackieControlProtocol::route_is_locked_to_strip (boost::shared_ptr<Route> r) const
+MackieControlProtocol::stripable_is_locked_to_strip (boost::shared_ptr<Stripable> 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<Route> & a, const boost::shared_ptr<Route> & b) const
+ bool operator () (const boost::shared_ptr<Stripable> & a, const boost::shared_ptr<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();
}
- 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<RouteList> routes = session->get_routes();
- set<uint32_t> remote_ids;
+ // fetch all stripables
+ StripableList stripables;
- // routes with remote_id 0 should never be added
- // TODO verify this with ardour devs
- // remote_ids.insert (0);
+ session->get_stripables (stripables);
- // sort in remote_id order, and exclude master, control and hidden routes
- // and any routes that are already set.
+ // sort in presentation order, and exclude master, control and hidden stripables
+ // and any stripables that are already set.
- for (RouteList::iterator it = routes->begin(); it != routes->end(); ++it) {
+ for (StripableList::iterator it = stripables.begin(); it != stripables.end(); ++it) {
- boost::shared_ptr<Route> route = *it;
-
- if (remote_ids.find (route->remote_control_id()) != remote_ids.end()) {
- continue;
- }
+ boost::shared_ptr<Stripable> 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;
}
- /* This next section which is not used yet, looks wrong to me
- The first four belong here but the bottom five are not a selection
- of routes and belong elsewhere as they are v-pot modes.
- */
+
switch (_view_mode) {
case Mixer:
+ if (!s->presentation_info().hidden()) {
+ sorted.push_back (s);
+ }
break;
case AudioTracks:
+ if (is_audio_track(s) && !s->presentation_info().hidden()) {
+ sorted.push_back (s);
+ }
break;
case Busses:
+ if (Profile->get_mixbus()) {
+#ifdef MIXBUS
+ if (s->mixbus()) {
+ sorted.push_back (s);
+ }
+#endif
+ } else {
+ if (!is_track(s) && !s->presentation_info().hidden()) {
+ sorted.push_back (s);
+ }
+ }
break;
case MidiTracks:
+ if (is_midi_track(s) && !s->presentation_info().hidden()) {
+ sorted.push_back (s);
+ }
break;
- case Loop:
+ case Plugins:
+ break;
+ 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->presentation_info().hidden())
+#else
+ if (!is_track(s) && !s->presentation_info().hidden())
+#endif
+ {
+ sorted.push_back (s);
+ }
+ break;
+ 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->presentation_info().selected() && !s->presentation_info().hidden()) {
+ sorted.push_back (s);
+ }
break;
}
-
- sorted.push_back (*it);
- remote_ids.insert (route->remote_control_id());
}
- sort (sorted.begin(), sorted.end(), RouteByRemoteId());
+ sort (sorted.begin(), sorted.end(), StripableByPresentationOrder());
return sorted;
}
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<boost::shared_ptr<Route> > routes;
+ vector<boost::shared_ptr<Stripable> > 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);
}
- }
-
- /* 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);
+ } 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<boost::shared_ptr<Stripable> > 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
BaseUI::run ();
- if (create_surfaces ()) {
- return -1;
- }
connect_session_signals ();
- update_surfaces ();
- /* set up periodic task for metering and automation
+ if (!_device_info.name().empty()) {
+ set_device (_device_info.name(), true);
+ }
+
+ /* set up periodic task for timecode display and metering and automation
*/
Glib::RefPtr<Glib::TimeoutSource> 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 */
+ /* periodic task used to update strip displays */
Glib::RefPtr<Glib::TimeoutSource> redisplay_timeout = Glib::TimeoutSource::create (10); // milliseconds
redisplay_connection = redisplay_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::redisplay));
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 ();
}
}
- update_timecode_display ();
-
return true;
}
Glib::Threads::Mutex::Lock lm (surfaces_lock);
for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
- (*s)->redisplay (now);
+ (*s)->redisplay (now, false);
}
}
void
MackieControlProtocol::update_global_button (int id, LedState ls)
{
- Glib::Threads::Mutex::Lock lm (surfaces_lock);
+ boost::shared_ptr<Surface> surface;
- if (!_device_info.has_global_controls()) {
- return;
+ {
+ Glib::Threads::Mutex::Lock lm (surfaces_lock);
+
+ if (surfaces.empty()) {
+ return;
+ }
+
+ if (!_device_info.has_global_controls()) {
+ return;
+ }
+ // surface needs to be master surface
+ surface = _master_surface;
}
- // surface needs to be master surface
- boost::shared_ptr<Surface> surface = _master_surface;
map<int,Control*>::iterator x = surface->controls_by_device_independent_id.find (id);
if (x != surface->controls_by_device_independent_id.end()) {
{
Glib::Threads::Mutex::Lock lm (surfaces_lock);
+ if (surfaces.empty()) {
+ return;
+ }
+
if (!_device_info.has_global_controls()) {
return;
}
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_subview_mode (MackieControlProtocol::None, boost::shared_ptr<Stripable>());
+ set_flip_mode (Normal);
}
// send messages to surface to set controls to correct values
// 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");
}
MackieControlProtocol::connect_session_signals()
{
// receive routes added
- session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_route_added, this, _1), 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
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
MackieControlProtocol::set_profile (const string& profile_name)
{
- if (profile_name == "default") {
- /* reset to default */
- _device_profile = DeviceProfile (profile_name);
- }
-
map<string,DeviceProfile>::iterator d = DeviceProfile::device_profiles.find (profile_name);
if (d == DeviceProfile::device_profiles.end()) {
+ _device_profile = DeviceProfile (profile_name);
return;
}
/* 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)) {
- cerr << "Unknown device name\n";
return -1;
}
hui_timeout->attach (main_loop()->get_context());
}
- if (create_surfaces ()) {
- return -1;
- }
-
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);
}
- switch_banks (0, true);
+ if (create_surfaces ()) {
+ return -1;
+ }
DeviceChanged ();
{
string device_name;
surface_type_t stype = mcu; // type not yet determined
- char buf[128];
- if (_device_info.extenders() == 0) {
- device_name = X_("mackie control");
+ DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces for %2\n", 1 + _device_info.extenders(), _device_info.name()));
+
+ 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;
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n", 1 + _device_info.extenders()));
+ 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");
+ }
- 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> surface;
- if (n == _device_info.master_position()) {
+ if (is_master) {
stype = mcu;
} else {
stype = ext;
return -1;
}
- if (n == _device_info.master_position()) {
+ if (is_master) {
_master_surface = surface;
}
- if (_surfaces_state) {
- surface->set_state (*_surfaces_state, _surfaces_version);
+ 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 const * 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);
+ }
+ }
}
{
surfaces.push_back (surface);
}
- 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;
- }
-
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,
ARDOUR::DataType::MIDI,
session->engine().make_port_name_non_relative (surface->port().output_port().name())
);
-
- session->BundleAddedOrRemoved ();
-
- } else {
- _input_bundle.reset ((ARDOUR::Bundle*) 0);
- _output_bundle.reset ((ARDOUR::Bundle*) 0);
-
- session->BundleAddedOrRemoved ();
}
MIDI::Port& input_port (surface->port().input_port());
}
}
+ 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
MackieControlProtocol::close()
{
+ port_connection.disconnect ();
session_connections.drop_connections ();
- route_connections.drop_connections ();
+ stripable_connections.drop_connections ();
periodic_connection.disconnect ();
clear_surfaces();
}
+/** 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()
{
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;
}
+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)
{
}
if ((prop = node.property (X_("device-profile"))) != 0) {
- set_profile (prop->value());
+ if (prop->value().empty()) {
+ string default_profile_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 {
+ if (profile_exists (prop->value())) {
+ set_profile (prop->value());
+ } else {
+ set_profile (DeviceProfile::default_profile_name);
+ }
+ }
}
- XMLNode* snode = node.child (X_("Surfaces"));
+ XMLNode* dnode = node.child (X_("Configurations"));
- delete _surfaces_state;
- _surfaces_state = 0;
+ delete configuration_state;
+ configuration_state = 0;
- if (snode) {
- _surfaces_state = new XMLNode (*snode);
- _surfaces_version = version;
+ if (dnode) {
+ configuration_state = new XMLNode (*dnode);
+ state_version = version;
}
- switch_banks (bank, true);
+ (void) switch_banks (bank, true);
DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::set_state done\n");
// 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:
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));
}
}
-// RouteList is the set of routes that have just been added
void
-MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl)
+MackieControlProtocol::notify_stripable_removed ()
+{
+ Glib::Threads::Mutex::Lock lm (surfaces_lock);
+ for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
+ (*s)->master_monitor_may_have_changed ();
+ }
+}
+
+void
+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);
+
+ 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.
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
{
Glib::Threads::Mutex::Lock lm (surfaces_lock);
+
+ if (surfaces.empty()) {
+ return;
+ }
+
surface = _master_surface;
}
}
void
-MackieControlProtocol::notify_remote_id_changed()
+MackieControlProtocol::notify_presentation_info_changed ()
{
- Sorted sorted = get_sorted_routes();
+ {
+ Glib::Threads::Mutex::Lock lm (surfaces_lock);
+
+ if (surfaces.empty()) {
+ return;
+ }
+ }
+
+ Sorted sorted = get_sorted_stripables();
uint32_t sz = n_strips();
// if a remote id has been moved off the end, we need to shift
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));
+ if (sorted.size() < sz) { // avoid unsigned math mistake below
+ (void) switch_banks(0, true);
+ } else {
+ (void) switch_banks (max((Sorted::size_type) 0, sorted.size() - sz), true);
+ }
} else {
// Otherwise just refresh the current bank
refresh_current_bank();
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 ();
}
{
Glib::Threads::Mutex::Lock lm (surfaces_lock);
+ if (surfaces.empty()) {
+ return;
+ }
surface = _master_surface;
}
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);
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);
-
}
void
return;
}
+ 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;
+ }
+
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())));
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.
}
}
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("data available on %1\n", port->name()));
+ // DEBUG_TRACE (DEBUG::MackieControl, string_compose ("data available on %1\n", port->name()));
framepos_t now = session->engine().sample_time();
port->parse (now);
}
}
}
+void
+MackieControlProtocol::notify_subview_stripable_deleted ()
+{
+ /* return to global/mixer view */
+ _subview_stripable.reset ();
+ set_view_mode (Mixer);
+}
+
+bool
+MackieControlProtocol::subview_mode_would_be_ok (SubViewMode mode, boost::shared_ptr<Stripable> r)
+{
+ switch (mode) {
+ case None:
+ return true;
+ break;
+
+ case Sends:
+ if (r && r->send_level_controllable (0)) {
+ return true;
+ }
+ break;
+
+ case EQ:
+ if (r && r->eq_band_cnt() > 0) {
+ return true;
+ }
+ break;
+
+ case Dynamics:
+ if (r && r->comp_enable_controllable()) {
+ return true;
+ }
+ break;
+
+ case TrackView:
+ if (r) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+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 = copy.begin(); s != copy.end(); ++s) {
+ (*s)->subview_mode_changed ();
+ }
+
+ /* don't call this again from a timeout */
+ return false;
+}
+
+int
+MackieControlProtocol::set_subview_mode (SubViewMode sm, boost::shared_ptr<Stripable> r)
+{
+ 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));
+
+ if (_flip_mode != Normal) {
+ set_flip_mode (Normal);
+ }
+
+ if (!subview_mode_would_be_ok (sm, r)) {
+
+ 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<Glib::TimeoutSource> 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());
+ }
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ boost::shared_ptr<Stripable> old_stripable = _subview_stripable;
+
+ _subview_mode = sm;
+ _subview_stripable = r;
+
+ 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);
+ }
+ }
+
+ redisplay_subview_mode ();
+
+ /* turn buttons related to vpot mode on or off as required */
+
+ 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 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::Pan, off);
+ break;
+ 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::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)
{
- Glib::Threads::Mutex::Lock lm (surfaces_lock);
+ 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;
- for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
- (*s)->update_view_mode_display ();
+ 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<Stripable>());
+ display_view_mode ();
}
void
-MackieControlProtocol::set_flip_mode (FlipMode fm)
+MackieControlProtocol::display_view_mode ()
{
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 ();
+ (*s)->update_view_mode_display (true);
}
}
void
-MackieControlProtocol::set_pot_mode (PotMode m)
+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);
- _pot_mode = m;
+ _flip_mode = fm;
for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
- (*s)->update_potmode ();
-
+ (*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<Route> r, uint32_t surface, uint32_t strip_number)
+MackieControlProtocol::force_special_stripable_to_strip (boost::shared_ptr<Stripable> r, uint32_t surface, uint32_t strip_number)
{
if (!r) {
return;
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 ();
}
}
}
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);
-}
+ fader_automation_connections.drop_connections ();
-void
-MackieControlProtocol::_gui_track_selection_changed (ARDOUR::RouteNotificationList* rl, bool save_list)
-{
- /* We need to keep a list of the most recently selected routes around,
- but we are not allowed to keep shared_ptr<Route> unless we want to
- handle the complexities of route deletion. So instead, the GUI sends
- us a notification using weak_ptr<Route>, which we keep a copy
- of. For efficiency's sake, however, we convert the weak_ptr's into
- shared_ptr<Route> before passing them to however many surfaces (and
- thus strips) that we have.
- */
+ boost::shared_ptr<Stripable> r = first_selected_stripable ();
- StrongRouteNotificationList srl;
-
- for (ARDOUR::RouteNotificationList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
- boost::shared_ptr<ARDOUR::Route> 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<Stripable> r = first_selected_stripable ();
+
+ 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 (save_list) {
- _last_selected_routes = *rl;
+ 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;
}
}
}
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.front()->name()));
- if (!routes.empty()) {
- for (RouteList::iterator r = routes.begin(); r != routes.end(); ++r) {
+ if (stripables.empty()) {
+ return;
+ }
- if (main_modifier_state() == MODIFIER_CONTROL) {
- ToggleRouteSelection ((*r)->remote_control_id ());
+ if (stripables.size() == 1 && ControlProtocol::last_selected().size() == 1 && stripables.front()->presentation_info().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_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);
}
}
}
}
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);
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<Track> trk = boost::dynamic_pointer_cast<Track> (*r);
- if (trk) {
- controls.push_back (trk->rec_enable_control());
+ for (StripableList::iterator s = stripables.begin(); s != stripables.end(); ++s) {
+ boost::shared_ptr<AutomationControl> ac = (*s)->rec_enable_control();
+ if (ac) {
+ controls.push_back (ac);
}
}
break;
};
void
-MackieControlProtocol::pull_route_range (DownButtonList& down, RouteList& selected)
+MackieControlProtocol::pull_stripable_range (DownButtonList& down, StripableList& selected, uint32_t pressed)
{
ButtonRangeSorter cmp;
(*s)->number(), fs, ls));
for (uint32_t n = fs; n < ls; ++n) {
- boost::shared_ptr<Route> r = (*s)->nth_strip (n)->route();
+ Strip* strip = (*s)->nth_strip (n);
+ boost::shared_ptr<Stripable> 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
if (create_surfaces ()) {
return -1;
}
- switch_banks (_current_initial_bank, true);
+ (void) switch_banks (_current_initial_bank, true);
needs_ipmidi_restart = false;
return 0;
}
void
MackieControlProtocol::clear_surfaces ()
{
- port_connection.disconnect ();
clear_ports ();
{
}
}
+boost::shared_ptr<Surface>
+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<Surface> ();
+}
+
boost::shared_ptr<Surface>
MackieControlProtocol::nth_surface (uint32_t n) const
{
void
MackieControlProtocol::connection_handler (boost::weak_ptr<ARDOUR::Port> wp1, std::string name1, boost::weak_ptr<ARDOUR::Port> wp2, std::string name2, bool yn)
{
- Glib::Threads::Mutex::Lock lm (surfaces_lock);
+ Surfaces scopy;
+ {
+ Glib::Threads::Mutex::Lock lm (surfaces_lock);
+ scopy = surfaces;
+ }
- for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
+ for (Surfaces::const_iterator s = scopy.begin(); s != scopy.end(); ++s) {
if ((*s)->connection_handler (wp1, name1, wp2, name2, yn)) {
- cerr << (*s)->name() << " Connected, or disconnected\n";
ConnectionChange (*s);
break;
}
}
}
+
+bool
+MackieControlProtocol::is_track (boost::shared_ptr<Stripable> r) const
+{
+ return boost::dynamic_pointer_cast<Track>(r) != 0;
+}
+
+bool
+MackieControlProtocol::is_audio_track (boost::shared_ptr<Stripable> r) const
+{
+ return boost::dynamic_pointer_cast<AudioTrack>(r) != 0;
+}
+
+bool
+MackieControlProtocol::is_midi_track (boost::shared_ptr<Stripable> r) const
+{
+ return boost::dynamic_pointer_cast<MidiTrack>(r) != 0;
+}
+
+bool
+MackieControlProtocol::is_mapped (boost::shared_ptr<Stripable> r) const
+{
+ Glib::Threads::Mutex::Lock lm (surfaces_lock);
+
+ for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
+ if ((*s)->stripable_is_mapped (r)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+MackieControlProtocol::update_selected (boost::shared_ptr<Stripable> s, bool became_selected)
+{
+ if (became_selected) {
+
+ 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.
+ */
+
+ if (set_subview_mode (_subview_mode, first_selected_stripable())) {
+ set_subview_mode (None, boost::shared_ptr<Stripable>());
+ }
+
+ }
+}
+
+boost::shared_ptr<Stripable>
+MackieControlProtocol::first_selected_stripable () const
+{
+ boost::shared_ptr<Stripable> 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<Stripable>
+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) {
+ if ((*s).get() == strip.surface()) {
+ return global + strip.index();
+ }
+ global += (*s)->n_strips ();
+ }
+
+ return global;
+}
+
+void*
+MackieControlProtocol::request_factory (uint32_t num_requests)
+{
+ /* AbstractUI<T>::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<Stripable> r = first_selected_stripable ();
+
+ if (!r) {
+ return;
+ }
+
+ boost::shared_ptr<AutomationControl> ac = r->gain_control();
+
+ if (!ac) {
+ return;
+ }
+
+ ac->set_automation_state (as);
+}