void discover_control_protocols ();
void foreach_known_protocol (boost::function<void(const ControlProtocolInfo*)>);
void load_mandatory_protocols ();
+ void midi_connectivity_established ();
ControlProtocol* instantiate (ControlProtocolInfo&);
int teardown (ControlProtocolInfo&);
-
+
std::list<ControlProtocolInfo*> control_protocol_info;
static const std::string state_node_name;
return *_instance;
}
+
+void
+ControlProtocolManager::midi_connectivity_established ()
+{
+ Glib::Mutex::Lock lm (protocols_lock);
+
+ for (list<ControlProtocol*>::iterator p = control_protocols.begin(); p != control_protocols.end(); ++p) {
+ (*p)->midi_connectivity_established ();
+ }
+}
BootMessage (_("Setup signal flow and plugins"));
+ /* this will cause the CPM to instantiate any protocols that are in use
+ * (or mandatory), which will pass it this Session, and then call
+ * set_state() on each instantiated protocol to match stored state.
+ */
+
ControlProtocolManager::instance().set_session (this);
/* This must be done after the ControlProtocolManager set_session above,
as it will set states for ports which the ControlProtocolManager creates.
*/
+
MIDI::Manager::instance()->set_port_states (Config->midi_port_states ());
/* And this must be done after the MIDI::Manager::set_port_states as
hookup_io ();
+ /* Let control protocols know that we are now all connected, so they
+ * could start talking to surfaces if they want to.
+ */
+
+ ControlProtocolManager::instance().midi_connectivity_established ();
+
if (_is_new && !no_auto_connect()) {
Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock());
auto_connect_master_bus ();
virtual void route_list_changed () {}
+ virtual void midi_connectivity_established () {}
+
PBD::Signal0<void> ActiveChanged;
/* signals that a control protocol can emit and other (presumably graphical)
void
Control::set_value (float val)
{
- normal_ac->set_value (normal_ac->interface_to_internal (val));
+ if (normal_ac) {
+ normal_ac->set_value (normal_ac->interface_to_internal (val));
+ }
}
float
Control::get_value ()
{
+ if (!normal_ac) {
+ return 0.0f;
+ }
return normal_ac->internal_to_interface (normal_ac->get_value());
}
void
Control::start_touch (double when)
{
- return normal_ac->start_touch (when);
+ if (normal_ac) {
+ return normal_ac->start_touch (when);
+ }
}
void
Control::stop_touch (double when, bool mark)
{
- return normal_ac->stop_touch (when, mark);
+ if (normal_ac) {
+ return normal_ac->stop_touch (when, mark);
+ }
}
#include "gtkmm2ext/utils.h"
#include "gtkmm2ext/actions.h"
+#include "ardour/rc_configuration.h"
+
#include "mackie_control_protocol.h"
#include "device_info.h"
#include "gui.h"
void
MackieControlProtocolGUI::profile_combo_changed ()
{
- _cp.set_profile (_profile_combo.get_active_text());
- refresh_function_key_editor ();
-}
+ string profile = _profile_combo.get_active_text();
+ _cp.set_profile (profile);
+ ARDOUR::Config->set_mackie_device_profile (profile);
+ refresh_function_key_editor ();
+}
/*
Copyright (C) 2006,2007 John Anderson
+ Copyright (C) 2012 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
set_device (Config->get_mackie_device_name());
set_profile (Config->get_mackie_device_profile());
- AudioEngine::instance()->PortConnectedOrDisconnected.connect (
- audio_engine_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::port_connected_or_disconnected, this, _2, _4, _5),
- this
- );
-
TrackSelectionChanged.connect (gui_connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::gui_track_selection_changed, this, _1), this);
_instance = this;
}
}
+void
+MackieControlProtocol::midi_connectivity_established ()
+{
+ /* may need to tell surfaces because they may need to wake up the
+ * device
+ */
+}
+
// go to the previous track.
// Assume that get_sorted_routes().size() > route_table.size()
void
_current_initial_bank = initial;
_current_selected_track = -1;
- for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) {
- (*si)->drop_routes ();
- }
-
// Map current bank of routes onto each surface(+strip)
if (_current_initial_bank <= sorted.size()) {
return;
}
+
+
// do the initial bank switch to connect signals
// _current_initial_bank is initialised by set_state
switch_banks (_current_initial_bank, true);
boost::shared_ptr<Surface> surface = surfaces.front();
- if (surface->type() != mcu || !surface->has_timecode_display()) {
+ if (surface->type() != mcu || !_device_info.has_timecode_display()) {
return;
}
return b;
}
-void
-MackieControlProtocol::port_connected_or_disconnected (string a, string b, bool connected)
-{
- /* If something is connected to one of our output ports, send MIDI to update the surface
- to whatever state it should have.
- */
-
- if (!connected) {
- return;
- }
-
- for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
- string const n = AudioEngine::instance()->make_port_name_non_relative ((*s)->port().output_port().name ());
- if (a == n || b == n) {
- update_surfaces ();
- return;
- }
- }
-}
-
void
MackieControlProtocol::do_request (MackieControlUIRequest* req)
{
int stop ();
void thread_init ();
-
- /* handling function key presses */
-
- void f_press (uint32_t fn);
+ void midi_connectivity_established ();
private:
ButtonMap button_map;
void create_surfaces ();
- void port_connected_or_disconnected (std::string, std::string, bool);
bool periodic();
void build_gui ();
bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
// can use first_mark_before/after as well
unsigned long elapsed = _frm_left_last.restart();
- Location * loc = session->locations()->first_location_before (
- session->transport_frame()
- );
+ Location * loc = session->locations()->first_location_before (session->transport_frame());
// allow a quick double to go past a previous mark
if (session->transport_rolling() && elapsed < 500 && loc != 0) {
// move to the location, if it's valid
if (loc != 0) {
session->request_locate (loc->start(), session->transport_rolling());
+ } else {
+ session->request_locate (session->locations()->session_range_location()->start(), session->transport_rolling());
}
return on;
if (loc != 0) {
session->request_locate (loc->start(), session->transport_rolling());
+ } else {
+ session->request_locate (session->locations()->session_range_location()->end(), session->transport_rolling());
}
return on;
LedState
MackieControlProtocol::rewind_press (Button &)
{
- rewind ();
+ if (_modifier_state == MODIFIER_CONTROL) {
+ goto_start ();
+ } else {
+ rewind ();
+ }
return none;
}
LedState
MackieControlProtocol::ffwd_press (Button &)
{
- ffwd ();
+ if (_modifier_state == MODIFIER_CONTROL) {
+ goto_end();
+ } else {
+ ffwd ();
+ }
return none;
}
return off;
}
-void
-MackieControlProtocol::f_press (uint32_t fn)
-{
-#if 0
- string action = f_action (0);
- if (!action.empty()) {
- access_action (action);
- }
-#endif
-}
LedState
MackieControlProtocol::F1_press (Button &)
{
- f_press (0);
return off;
}
LedState
LedState
MackieControlProtocol::F2_press (Button &)
{
- f_press (1);
return off;
}
LedState
LedState
MackieControlProtocol::F3_press (Button &)
{
- f_press (2);
return off;
}
LedState
LedState
MackieControlProtocol::F4_press (Button &)
{
- f_press (3);
return off;
}
LedState
LedState
MackieControlProtocol::F5_press (Button &)
{
- f_press (4);
return off;
}
LedState
LedState
MackieControlProtocol::F6_press (Button &)
{
- f_press (5);
return off;
}
LedState
LedState
MackieControlProtocol::F7_press (Button &)
{
- f_press (6);
return off;
}
LedState
}
MidiByteArray
-Pot::set_mode (Pot::Mode m)
-{
- mode = m;
- return update_message ();
-}
-
-MidiByteArray
-Pot::set_onoff (bool onoff)
-{
- on = onoff;
- return update_message ();
-}
-
-MidiByteArray
-Pot::set_all (float val, bool onoff, Mode m)
-{
- position = val;
- on = onoff;
- mode = m;
- return update_message ();
-}
-
-MidiByteArray
-Pot::update_message ()
+Pot::set (float val, bool onoff, Mode mode)
{
// TODO do an exact calc for 0.50? To allow manually re-centering the port.
// center on or off
- MIDI::byte msg = (position > 0.45 && position < 0.55 ? 1 : 0) << 6;
+ MIDI::byte msg = (val > 0.45 && val < 0.55 ? 1 : 0) << 6;
// mode
- msg |= (mode << 4);
+ msg |= (onoff << 4);
- // position, but only if off hasn't explicitly been set
+ // val, but only if off hasn't explicitly been set
- if (on) {
+ if (onoff) {
if (mode == spread) {
- msg += (lrintf (position * 6) + 1) & 0x0f; // 0b00001111
+ msg += (lrintf (val * 6) + 1) & 0x0f; // 0b00001111
} else {
- msg += (lrintf (position * 10.0) + 1) & 0x0f; // 0b00001111
+ msg += (lrintf (val * 10.0) + 1) & 0x0f; // 0b00001111
}
}
};
Pot (int id, std::string name, Group & group)
- : Control (id, name, group)
- , position (0.0)
- , mode (dot)
- , on (true) {}
+ : Control (id, name, group) {}
- MidiByteArray set_mode (Mode);
- MidiByteArray set_onoff (bool);
- MidiByteArray set_all (float, bool, Mode);
-
- MidiByteArray zero() { return set_all (0.0, on, mode); }
-
- MidiByteArray update_message ();
+ MidiByteArray set (float, bool, Mode);
+ MidiByteArray zero() { return set (0.0, false, Pot::spread); }
static Control* factory (Surface&, int id, const char*, Group&);
- private:
- float position;
- Mode mode;
- bool on;
};
}
}
void
-Strip::set_route (boost::shared_ptr<Route> r)
+Strip::set_route (boost::shared_ptr<Route> r, bool with_messages)
{
if (_controls_locked) {
return;
}
}
}
-
- current_pot_modes.push_back (Input);
- current_pot_modes.push_back (Output);
-
- if (_route->nth_send (0) != 0) {
- current_pot_modes.push_back (Send1);
- }
- if (_route->nth_send (1) != 0) {
- current_pot_modes.push_back (Send2);
- }
- if (_route->nth_send (2) != 0) {
- current_pot_modes.push_back (Send3);
- }
- if (_route->nth_send (3) != 0) {
- current_pot_modes.push_back (Send4);
- }
- if (_route->nth_send (4) != 0) {
- current_pot_modes.push_back (Send5);
- }
- if (_route->nth_send (5) != 0) {
- current_pot_modes.push_back (Send6);
- }
- if (_route->nth_send (6) != 0) {
- current_pot_modes.push_back (Send7);
- }
- if (_route->nth_send (7) != 0) {
- current_pot_modes.push_back (Send8);
- }
}
void
Strip::notify_all()
{
+ if (!_route) {
+ zero ();
+ return;
+ }
+
notify_solo_changed ();
notify_mute_changed ();
notify_gain_changed ();
void
Strip::notify_gain_changed (bool force_update)
{
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("gain changed for strip %1, flip mode %2\n", _index, _surface->mcp().flip_mode()));
-
if (_route) {
Control* control;
control = _fader;
}
- if (!control->in_use()) {
-
- boost::shared_ptr<AutomationControl> ac = _route->gain_control();
-
- float gain_coefficient = ac->get_value();
- float normalized_position = ac->internal_to_interface (gain_coefficient);
-
- if (force_update || normalized_position != _last_gain_position_written) {
-
- if (_surface->mcp().flip_mode()) {
- _surface->write (_vpot->set_all (normalized_position, true, Pot::wrap));
- do_parameter_display (GainAutomation, gain_coefficient);
- } else {
- _surface->write (_fader->set_position (normalized_position));
- do_parameter_display (GainAutomation, gain_coefficient);
+ boost::shared_ptr<AutomationControl> ac = _route->gain_control();
+
+ float gain_coefficient = ac->get_value();
+ float normalized_position = ac->internal_to_interface (gain_coefficient);
+
+ if (force_update || normalized_position != _last_gain_position_written) {
+
+ if (_surface->mcp().flip_mode()) {
+ if (!control->in_use()) {
+ _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
}
-
- queue_display_reset (2000);
- _last_gain_position_written = normalized_position;
-
+ do_parameter_display (GainAutomation, gain_coefficient);
} else {
- DEBUG_TRACE (DEBUG::MackieControl, "value is stale, no message sent\n");
+ if (!control->in_use()) {
+ _surface->write (_fader->set_position (normalized_position));
+ }
+ do_parameter_display (GainAutomation, gain_coefficient);
}
- } else {
- DEBUG_TRACE (DEBUG::MackieControl, "fader in use, no message sent\n");
+
+ queue_display_reset (2000);
+ _last_gain_position_written = normalized_position;
}
- } else {
- DEBUG_TRACE (DEBUG::MackieControl, "no route or no fader\n");
}
}
} else {
line1 = PBD::short_version (fullname, 6);
}
-
+
_surface->write (display (0, line1));
}
}
_surface->write (_fader->set_position (pos));
do_parameter_display (PanAzimuthAutomation, pos);
} else {
- _surface->write (_vpot->set_all (pos, true, Pot::dot));
+ _surface->write (_vpot->set (pos, true, Pot::dot));
do_parameter_display (PanAzimuthAutomation, pos);
}
_surface->write (_fader->set_position (pos));
do_parameter_display (PanWidthAutomation, pos);
} else {
- _surface->write (_vpot->set_all (pos, true, Pot::spread));
+ _surface->write (_vpot->set (pos, true, Pot::spread));
do_parameter_display (PanWidthAutomation, pos);
}
}
}
-MidiByteArray
+void
Strip::zero ()
{
- MidiByteArray retval;
-
for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
- retval << (*it)->zero ();
+ _surface->write ((*it)->zero ());
}
- retval << blank_display (0);
- retval << blank_display (1);
-
- return retval;
+ _surface->write (blank_display (0));
+ _surface->write (blank_display (1));
}
MidiByteArray
// sysex trailer
retval << MIDI::eox;
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display midi: %1\n", retval));
-
return retval;
}
void
Strip::reset_display ()
{
- _surface->write (display (1, vpot_mode_string()));
+ if (_route) {
+ _surface->write (display (1, vpot_mode_string()));
+ } else {
+ _surface->write (blank_display (1));
+ }
+
clear_display_reset ();
}
/* do not change vpot mode while in flipped mode */
DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
_surface->write (display (1, "Flip"));
- queue_display_reset (2000);
+ queue_display_reset (1000);
return;
}
if (pannable) {
_fader->set_control (pannable->pan_azimuth_control);
}
- _vpot->set_mode (Pot::boost_cut);
_vpot_mode = Gain;
} else {
/* gain to fader, pan azi to vpot */
_fader->set_control (_route->gain_control());
if (pannable) {
- _vpot->set_mode (Pot::dot);
_vpot->set_control (pannable->pan_azimuth_control);
}
}
if (pannable) {
_fader->set_control (pannable->pan_width_control);
}
- _vpot->set_mode (Pot::boost_cut);
_vpot_mode = Gain;
} else {
/* gain to fader, pan width to vpot */
_fader->set_control (_route->gain_control());
if (pannable) {
- _vpot->set_mode (Pot::spread);
_vpot->set_control (pannable->pan_width_control);
}
}
void add (Control & control);
int index() const { return _index; } // zero based
- void set_route (boost::shared_ptr<ARDOUR::Route>);
+ void set_route (boost::shared_ptr<ARDOUR::Route>, bool with_messages = true);
// call all signal handlers manually
void notify_all();
MidiByteArray display (uint32_t line_number, const std::string&);
MidiByteArray blank_display (uint32_t line_number);
- MidiByteArray zero ();
+
+ void zero ();
void flip_mode_changed (bool notify=false);
, _stype (stype)
, _number (number)
, _name (device_name)
- , _active (true)
+ , _active (false)
, _connected (false)
, _jog_wheel (0)
{
connect_to_signals ();
- /* wakey wakey */
-
- MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
- _port->write (wakeup);
- wakeup[4] = 0x15; /* wakup Mackie XT */
- _port->write (wakeup);
- wakeup[4] = 0x10; /* wakupe Logic Control */
- _port->write (wakeup);
- wakeup[4] = 0x11; /* wakeup Logic Control XT */
- _port->write (wakeup);
-
DEBUG_TRACE (DEBUG::MackieControl, "Surface::init finish\n");
}
{
DEBUG_TRACE (DEBUG::MackieControl, "Surface: destructor\n");
- // faders to minimum
- write_sysex (0x61);
- // All LEDs off
- write_sysex (0x62);
- // Reset (reboot into offline mode)
- // _write_sysex (0x63);
+ zero_all ();
// delete groups
for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
}
delete _jog_wheel;
- delete _port;
+
+ /* don't delete the port, because we want its output to remain queued */
}
const MidiByteArray&
if (control) {
Pot* pot = dynamic_cast<Pot*> (control);
if (pot) {
- _port->write (pot->set_onoff (false));
+ _port->write (pot->set (0.0, false, Pot::spread));
}
}
}
-bool
-Surface::has_timecode_display () const
-{
- return false;
-}
-
float
Surface::scrub_scaling_factor () const
{
{
MidiByteArray bytes (count, raw_bytes);
-
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
/* always save the device type ID so that our outgoing sysex messages
if (bytes[4] == 0x10 || bytes[4] == 0x11) {
write_sysex (host_connection_query (bytes));
} else {
- _active = true;
+ if (!_active) {
+ _active = true;
+ std::cerr << "Surface " << _number << " Now active!\n";
+ zero_controls ();
+ for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
+ (*s)->notify_all ();
+ }
+ update_view_mode_display ();
+ }
}
break;
_port->write (buf);
}
-void
-Surface::drop_routes ()
-{
- for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
- (*s)->set_route (boost::shared_ptr<Route>());
- }
-}
-
uint32_t
Surface::n_strips () const
{
{
// TODO turn off Timecode displays
+ std::cerr << "Surface " << number() << " ZERO\n";
+
// zero all strips
for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
- _port->write ((*it)->zero());
+ (*it)->zero();
+ }
+
+ zero_controls ();
+}
+
+void
+Surface::zero_controls ()
+{
+ if (_stype != mcu || !_mcp.device_info().has_global_controls()) {
+ return;
}
// turn off global buttons and leds
}
}
- // any hardware-specific stuff
- // clear 2-char display
- _port->write (two_char_display (" "));
+ if (_number == 0 && _mcp.device_info().has_two_character_display()) {
+ // any hardware-specific stuff
+ // clear 2-char display
+ _port->write (two_char_display ("aa"));
+ }
// and the led ring for the master strip
blank_jog_ring ();
for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
(*s)->periodic (now_usecs);
}
-
}
void
Surface::write (const MidiByteArray& data)
{
- _port->write (data);
+ if (_active) {
+ _port->write (data);
+ }
}
void
vector<boost::shared_ptr<Route> >::const_iterator r;
Strips::iterator s;
- for (s = strips.begin(); s != strips.end(); ++s) {
- (*s)->set_route (boost::shared_ptr<Route>());
- }
-
for (r = routes.begin(), s = strips.begin(); r != routes.end() && s != strips.end(); ++r, ++s) {
(*s)->set_route (*r);
}
+
+ for (; s != strips.end(); ++s) {
+ (*s)->set_route (boost::shared_ptr<Route>());
+ }
+
+
}
static char translate_seven_segment (char achar)
MidiByteArray
Surface::two_char_display (const std::string & msg, const std::string & dots)
{
- if (_stype != mcu) {
+ if (_stype != mcu || !_mcp.device_info().has_two_character_display()) {
return MidiByteArray();
}
void
Surface::display_timecode (const std::string & timecode, const std::string & timecode_last)
{
- if (has_timecode_display()) {
+ if (_active && _mcp.device_info().has_timecode_display()) {
_port->write (timecode_display (timecode, timecode_last));
}
}
string text;
Button* button = 0;
+ if (!_active) {
+ return;
+ }
+
switch (_mcp.view_mode()) {
case MackieControlProtocol::Mixer:
_port->write (two_char_display ("Mx"));
}
}
+void
+Surface::say_hello ()
+{
+ /* wakey wakey */
+
+ MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
+ _port->write (wakeup);
+ wakeup[4] = 0x15; /* wakup Mackie XT */
+ _port->write (wakeup);
+ wakeup[4] = 0x10; /* wakupe Logic Control */
+ _port->write (wakeup);
+ wakeup[4] = 0x11; /* wakeup Logic Control XT */
+ _port->write (wakeup);
+
+ zero_all ();
+}
uint32_t number() const { return _number; }
const std::string& name() { return _name; }
+ void say_hello ();
+
bool active() const { return _active; }
void drop_routes ();
/// called from MackieControlProtocol::zero_all to turn things off
void zero_all ();
+ void zero_controls ();
/// turn off leds around the jog wheel. This is for surfaces that use a pot
/// pretending to be a jog wheel.
void blank_jog_ring ();
- bool has_timecode_display() const;
void display_timecode (const std::string & /*timecode*/, const std::string & /*timecode_last*/);
/**
}
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("port %1 write %2\n", output_port().name(), mba));
-
+
int count = output_port().write (mba.bytes().get(), mba.size(), 0);
if (count != (int)mba.size()) {