2 Copyright (C) 2012 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/convert.h>
28 #include "pbd/stacktrace.h"
30 #include "midi++/port.h"
32 #include "ardour/audioengine.h"
33 #include "ardour/automation_control.h"
34 #include "ardour/debug.h"
35 #include "ardour/route.h"
36 #include "ardour/panner.h"
37 #include "ardour/panner_shell.h"
38 #include "ardour/profile.h"
39 #include "ardour/rc_configuration.h"
40 #include "ardour/session.h"
41 #include "ardour/utils.h"
43 #include <gtkmm2ext/gui_thread.h>
45 #include "control_group.h"
46 #include "surface_port.h"
49 #include "mackie_control_protocol.h"
50 #include "jog_wheel.h"
62 #ifdef PLATFORM_WINDOWS
63 #define random() rand()
68 using ARDOUR::Stripable;
70 using ARDOUR::Profile;
71 using ARDOUR::AutomationControl;
72 using namespace ArdourSurface;
73 using namespace Mackie;
75 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
77 // The MCU sysex header.4th byte Will be overwritten
78 // when we get an incoming sysex that identifies
80 static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
82 // The MCU extender sysex header.4th byte Will be overwritten
83 // when we get an incoming sysex that identifies
85 static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x15);
88 // The MCU sysex header for QCon Control surface
89 static MidiByteArray mackie_sysex_hdr_qcon (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
91 // The MCU sysex header for QCon Control - extender
92 // The extender differs from Mackie by 4th bit - it's same like for main control surface (for display)
93 static MidiByteArray mackie_sysex_hdr_xt_qcon (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
96 static MidiByteArray empty_midi_byte_array;
98 Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype)
102 , _name (device_name)
107 , _last_master_gain_written (-0.0f)
108 , connection_state (0)
112 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
115 _port = new SurfacePort (*this);
117 throw failed_constructor ();
121 if( mcp.device_info().is_qcon() ) {
127 /* only the first Surface object has global controls */
128 /* lets use master_position instead */
129 uint32_t mp = _mcp.device_info().master_position();
131 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
132 if (_mcp.device_info().has_global_controls()) {
134 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
137 if (_mcp.device_info().has_master_fader()) {
139 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
143 uint32_t n = _mcp.device_info().strip_cnt();
147 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
150 if (_mcp.device_info().uses_ipmidi()) {
151 /* ipMIDI port already exists, we can just assume that we're
154 * If the user still hasn't connected the ipMIDI surface and/or
155 * turned it on, then they have to press "Discover Mackie
156 * Devices" in the GUI at the right time.
159 connection_state |= (InputConnected|OutputConnected);
163 connect_to_signals ();
165 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
170 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
173 g_source_destroy (input_source);
177 // delete groups (strips)
178 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
182 // delete controls (global buttons, master fader etc)
183 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
189 // the ports take time to release and we may be rebuilding right away
190 // in the case of changing devices.
192 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
196 Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
202 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
203 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
205 if (ni == name1 || ni == name2) {
207 connection_state |= InputConnected;
209 connection_state &= ~InputConnected;
211 } else if (no == name1 || no == name2) {
213 connection_state |= OutputConnected;
215 connection_state &= ~OutputConnected;
222 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
224 /* this will send a device query message, which should
225 result in a response that will kick off device type
226 discovery and activation of the surface(s).
228 The intended order of events is:
230 - each surface sends a device query message
231 - devices respond with either MCP or LCP response (sysex in both
233 - sysex message causes Surface::turn_it_on() which tells the
234 MCP object that the surface is ready, and sets up strip
235 displays and binds faders and buttons for that surface
237 In the case of LCP, where this is a handshake process that could
238 fail, the response process to the initial sysex after a device query
239 will mark the surface inactive, which won't shut anything down
240 but will stop any writes to the device.
242 Note: there are no known cases of the handshake process failing.
244 We actually can't initiate this in this callback, so we have
245 to queue it with the MCP event loop.
248 /* XXX this is a horrible hack. Without a short sleep here,
249 something prevents the device wakeup messages from being
250 sent and/or the responses from being received.
257 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
261 return true; /* connection status changed */
267 XMLNode* node = new XMLNode (X_("Surface"));
268 node->set_property (X_("name"), _name);
269 node->add_child_nocopy (_port->get_state());
274 Surface::set_state (const XMLNode& node, int version)
276 /* Look for a node named after the device we're part of */
278 XMLNodeList const& children = node.children();
281 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
283 if ((*c)->get_property (X_("name"), name) && name == _name) {
293 XMLNode* portnode = mynode->child (X_("Port"));
295 if (_port->set_state (*portnode, version)) {
304 Surface::sysex_hdr() const
308 if (_mcp.device_info().is_qcon()) {
309 return mackie_sysex_hdr_qcon;
311 return mackie_sysex_hdr;
314 if(_mcp.device_info().is_qcon()) {
315 return mackie_sysex_hdr_xt_qcon;
317 return mackie_sysex_hdr_xt;
320 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
321 return mackie_sysex_hdr;
324 static GlobalControlDefinition mackie_global_controls[] = {
325 { "external", Pot::External, Pot::factory, "none" },
326 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
327 { "timecode", Led::Timecode, Led::factory, "none" },
328 { "beats", Led::Beats, Led::factory, "none" },
329 { "solo", Led::RudeSolo, Led::factory, "none" },
330 { "relay_click", Led::RelayClick, Led::factory, "none" },
331 { "", 0, Led::factory, "" }
335 Surface::init_controls()
339 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
340 groups["assignment"] = new Group ("assignment");
341 groups["automation"] = new Group ("automation");
342 groups["bank"] = new Group ("bank");
343 groups["cursor"] = new Group ("cursor");
344 groups["display"] = new Group ("display");
345 groups["function select"] = new Group ("function select");
346 groups["global view"] = new Group ("global view");
347 groups["master"] = new Group ("master");
348 groups["modifiers"] = new Group ("modifiers");
349 groups["none"] = new Group ("none");
350 groups["transport"] = new Group ("transport");
351 groups["user"] = new Group ("user");
352 groups["utilities"] = new Group ("utilities");
354 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
355 if (_mcp.device_info().has_jog_wheel()) {
356 _jog_wheel = new Mackie::JogWheel (_mcp);
359 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
360 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
361 group = groups[mackie_global_controls[n].group_name];
362 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
363 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
366 /* add global buttons */
367 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
368 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
370 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
371 group = groups[b->second.group];
372 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
377 Surface::init_strips (uint32_t n)
379 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
381 for (uint32_t i = 0; i < n; ++i) {
385 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
387 Strip* strip = new Strip (*this, name, i, strip_buttons);
389 groups[name] = strip;
390 strips.push_back (strip);
395 Surface::master_monitor_may_have_changed ()
397 if (_number == _mcp.device_info().master_position()) {
403 Surface::setup_master ()
405 boost::shared_ptr<Stripable> m;
407 if ((m = _mcp.get_session().monitor_out()) == 0) {
408 m = _mcp.get_session().master_out();
413 _master_fader->set_control (boost::shared_ptr<AutomationControl>());
415 master_connection.disconnect ();
419 if (!_master_fader) {
420 Groups::iterator group_it;
422 group_it = groups.find("master");
424 if (group_it == groups.end()) {
425 groups["master"] = master_group = new Group ("master");
427 master_group = group_it->second;
430 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *master_group));
432 DeviceInfo device_info = _mcp.device_info();
433 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
434 Button* bb = dynamic_cast<Button*> (Button::factory (
436 Button::MasterFaderTouch,
442 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
443 number(), Button::MasterFaderTouch, bb->id()));
445 master_connection.disconnect ();
448 _master_fader->set_control (m->gain_control());
449 m->gain_control()->Changed.connect (master_connection, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
450 _last_master_gain_written = FLT_MAX; /* some essentially impossible value */
451 master_gain_changed ();
455 Surface::master_gain_changed ()
457 if (!_master_fader) {
461 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
466 float normalized_position = ac->internal_to_interface (ac->get_value());
467 if (normalized_position == _last_master_gain_written) {
471 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
473 _port->write (_master_fader->set_position (normalized_position));
474 _last_master_gain_written = normalized_position;
478 Surface::scaled_delta (float delta, float current_speed)
480 /* XXX needs work before use */
481 const float sign = delta < 0.0 ? -1.0 : 1.0;
483 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
487 Surface::display_bank_start (uint32_t current_bank)
489 if (current_bank == 0) {
490 // send Ar. to 2-char display on the master
491 show_two_char_display ("Ar", "..");
493 // write the current first remote_id to the 2-char display
494 show_two_char_display (current_bank);
499 Surface::blank_jog_ring ()
501 Control* control = controls_by_device_independent_id[Jog::ID];
504 Pot* pot = dynamic_cast<Pot*> (control);
506 _port->write (pot->set (0.0, false, Pot::spread));
512 Surface::scrub_scaling_factor () const
518 Surface::connect_to_signals ()
523 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
524 number(), _port->input_port().name()));
526 MIDI::Parser* p = _port->input_port().parser();
529 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
530 /* V-Pot messages are Controller */
531 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
532 /* Button messages are NoteOn */
533 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
534 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
535 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
536 /* Fader messages are Pitchbend */
538 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
539 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
542 p->channel_pitchbend[_mcp.device_info().strip_cnt()].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, _mcp.device_info().strip_cnt()));
549 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
551 /* Pitchbend messages are fader position messages. Nothing in the data we get
552 * from the MIDI::Parser conveys the fader ID, which was given by the
553 * channel ID in the status byte.
555 * Instead, we have used bind() to supply the fader-within-strip ID
556 * when we connected to the per-channel pitchbend events.
559 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
560 fader_id, pb, _number, pb/16384.0));
562 if (_mcp.device_info().no_handshake()) {
566 Fader* fader = faders[fader_id];
569 Strip* strip = dynamic_cast<Strip*> (&fader->group());
570 float pos = pb / 16384.0;
572 strip->handle_fader (*fader, pos);
574 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
576 fader->set_value (pos); // alter master gain
577 _port->write (fader->set_position (pos)); // write back value (required for servo)
580 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
585 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
587 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
589 if (_mcp.device_info().no_handshake()) {
593 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
597 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
601 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
602 Fader* fader = faders[ev->note_number];
604 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
608 Strip* strip = dynamic_cast<Strip*> (&fader->group());
610 if (ev->velocity > 64) {
611 strip->handle_fader_touch (*fader, true);
613 strip->handle_fader_touch (*fader, false);
619 Button* button = buttons[ev->note_number];
623 if (ev->velocity > 64) {
627 Strip* strip = dynamic_cast<Strip*> (&button->group());
630 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
631 strip->index(), button->name(), (ev->velocity > 64)));
632 strip->handle_button (*button, ev->velocity > 64 ? press : release);
635 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
636 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
639 if (ev->velocity <= 64) {
644 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
647 /* button release should reset timer AFTER handler(s) have run */
651 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
653 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
655 if (_mcp.device_info().no_handshake()) {
659 Pot* pot = pots[ev->controller_number];
661 // bit 6 gives the sign
662 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
663 // bits 0..5 give the velocity. we interpret this as "ticks
664 // moved before this message was sent"
665 float ticks = (ev->value & 0x3f);
667 /* euphonix and perhaps other devices send zero
668 when they mean 1, we think.
674 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_SHIFT) {
675 delta = sign * (ticks / (float) 0xff);
677 delta = sign * (ticks / (float) 0x3f);
681 if (ev->controller_number == Jog::ID && _jog_wheel) {
683 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
684 _jog_wheel->jog_event (delta);
687 // add external (pedal?) control here
692 Strip* strip = dynamic_cast<Strip*> (&pot->group());
694 strip->handle_pot (*pot, delta);
699 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
701 MidiByteArray bytes (count, raw_bytes);
703 if (_mcp.device_info().no_handshake()) {
707 /* always save the device type ID so that our outgoing sysex messages
712 if (_mcp.device_info().is_qcon()) {
713 mackie_sysex_hdr_qcon[4] = bytes[4];
715 mackie_sysex_hdr[4] = bytes[4];
719 if (_mcp.device_info().is_qcon()) {
720 mackie_sysex_hdr_xt_qcon[4] = bytes[4];
722 mackie_sysex_hdr_xt[4] = bytes[4];
729 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
732 LCP: Connection Challenge
734 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
735 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
736 write_sysex (host_connection_query (bytes));
739 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
747 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
749 /* Behringer X-Touch Compact: Device Ready
751 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Behringer X-Touch Compact ready, current status = %1\n", _active));
755 case 0x03: /* LCP Connection Confirmation */
756 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
757 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
758 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
759 write_sysex (host_connection_confirmation (bytes));
764 case 0x04: /* LCP: Confirmation Denied */
765 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
766 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
771 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
772 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("unknown device ID byte %1", (int) bytes[5]));
773 error << "MCP: unknown sysex: " << bytes << endmsg;
778 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
781 back_insert_iterator<MidiByteArray> back (l);
782 copy (begin, end, back);
784 MidiByteArray retval;
786 // this is how to calculate the response to the challenge.
787 // from the Logic docs.
788 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
789 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
790 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
791 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
797 Surface::host_connection_query (MidiByteArray & bytes)
799 MidiByteArray response;
801 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
802 /* not a Logic Control device - no response required */
806 // handle host connection query
807 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
809 if (bytes.size() != 18) {
810 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
814 // build and send host connection reply
816 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
817 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
822 Surface::host_connection_confirmation (const MidiByteArray & bytes)
824 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
826 // decode host connection confirmation
827 if (bytes.size() != 14) {
829 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
830 throw MackieControlException (os.str());
833 // send version request
834 return MidiByteArray (2, 0x13, 0x00);
838 Surface::turn_it_on ()
846 _mcp.device_ready ();
848 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
852 update_view_mode_display (false);
854 // if (_mcp.device_info ().has_global_controls ()) {
855 // _mcp.update_global_button (Button::Read, _mcp.metering_active ());
860 Surface::write_sysex (const MidiByteArray & mba)
867 buf << sysex_hdr() << mba << MIDI::eox;
872 Surface::write_sysex (MIDI::byte msg)
875 buf << sysex_hdr() << msg << MIDI::eox;
880 Surface::n_strips (bool with_locked_strips) const
882 if (with_locked_strips) {
883 return strips.size();
888 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
889 if (!(*it)->locked()) {
897 Surface::nth_strip (uint32_t n) const
899 if (n > n_strips()) {
908 if (_mcp.device_info().has_timecode_display ()) {
909 display_timecode (string (10, '0'), string (10, ' '));
912 if (_mcp.device_info().has_two_character_display()) {
913 show_two_char_display (string (2, '0'), string (2, ' '));
916 if (_mcp.device_info().has_master_fader () && _master_fader) {
917 _port->write (_master_fader->zero ());
921 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
929 Surface::zero_controls ()
931 if (!_mcp.device_info().has_global_controls()) {
935 // turn off global buttons and leds
937 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
938 Control & control = **it;
939 if (!control.group().is_strip()) {
940 _port->write (control.zero());
944 // and the led ring for the master strip
947 _last_master_gain_written = 0.0f;
951 Surface::periodic (uint64_t now_usecs)
953 master_gain_changed();
954 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
955 (*s)->periodic (now_usecs);
960 Surface::redisplay (ARDOUR::microseconds_t now, bool force)
962 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
963 (*s)->redisplay (now, force);
968 Surface::write (const MidiByteArray& data)
973 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
978 Surface::update_strip_selection ()
980 Strips::iterator s = strips.begin();
981 for ( ; s != strips.end(); ++s) {
982 (*s)->update_selection_state();
987 Surface::map_stripables (const vector<boost::shared_ptr<Stripable> >& stripables)
989 vector<boost::shared_ptr<Stripable> >::const_iterator r;
990 Strips::iterator s = strips.begin();
992 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 stripables to %2 strips\n", stripables.size(), strips.size()));
994 for (r = stripables.begin(); r != stripables.end() && s != strips.end(); ++s) {
996 /* don't try to assign stripables to a locked strip. it won't
997 use it anyway, but if we do, then we get out of sync
998 with the proposed mapping.
1001 if (!(*s)->locked()) {
1002 (*s)->set_stripable (*r);
1007 for (; s != strips.end(); ++s) {
1008 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 being set to null stripable\n", (*s)->index()));
1009 (*s)->set_stripable (boost::shared_ptr<Stripable>());
1014 translate_seven_segment (char achar)
1016 achar = toupper (achar);
1018 if (achar >= 0x40 && achar <= 0x60) {
1019 return achar - 0x40;
1020 } else if (achar >= 0x21 && achar <= 0x3f) {
1028 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
1030 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
1034 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
1035 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
1037 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
1038 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
1040 _port->write (right);
1041 _port->write (left);
1045 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
1048 os << setfill('0') << setw(2) << value % 100;
1049 show_two_char_display (os.str());
1053 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
1055 //TODO: Fix for Qcon to correct timecode value if is over 1000 bars
1057 if (!_active || !_mcp.device_info().has_timecode_display()) {
1060 // if there's no change, send nothing, not even sysex header
1061 if (timecode == last_timecode) return;
1063 // length sanity checking
1064 string local_timecode = timecode;
1066 // truncate to 10 characters
1067 if (local_timecode.length() > 10) {
1068 local_timecode = local_timecode.substr (0, 10);
1071 // pad to 10 characters
1072 while (local_timecode.length() < 10) {
1073 local_timecode += " ";
1076 // translate characters.
1077 // Only the characters that actually changed are sent.
1078 int position = 0x3f;
1080 for (i = local_timecode.length () - 1; i >= 0; i--) {
1082 if (local_timecode[i] == last_timecode[i]) {
1085 MidiByteArray retval (2, 0xb0, position);
1086 retval << translate_seven_segment (local_timecode[i]);
1087 _port->write (retval);
1092 Surface::update_flip_mode_display ()
1094 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1095 (*s)->flip_mode_changed ();
1100 Surface::subview_mode_changed ()
1102 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1103 (*s)->subview_mode_changed ();
1108 Surface::update_view_mode_display (bool with_helpful_text)
1117 switch (_mcp.view_mode()) {
1118 case MackieControlProtocol::Mixer:
1119 show_two_char_display ("Mx");
1121 text = _("Mixer View");
1123 case MackieControlProtocol::AudioTracks:
1124 show_two_char_display ("AT");
1125 id = Button::AudioTracks;
1126 text = _("Audio Tracks");
1128 case MackieControlProtocol::MidiTracks:
1129 show_two_char_display ("MT");
1130 id = Button::MidiTracks;
1131 text = _("MIDI Tracks");
1133 case MackieControlProtocol::Plugins:
1134 show_two_char_display ("PL");
1135 id = Button::Plugin;
1136 text = _("Plugins");
1138 case MackieControlProtocol::Busses:
1139 show_two_char_display ("BS");
1140 id = Button::Busses;
1141 if (Profile->get_mixbus()) {
1142 text = _("Mixbusses");
1147 case MackieControlProtocol::Auxes:
1148 show_two_char_display ("Au");
1152 case MackieControlProtocol::Hidden:
1153 show_two_char_display ("HI");
1154 id = Button::Outputs;
1155 text = _("Hidden Tracks");
1157 case MackieControlProtocol::Selected:
1158 show_two_char_display ("SE");
1160 text = _("Selected Tracks");
1166 vector<int> view_mode_buttons;
1167 view_mode_buttons.push_back (Button::View);
1168 view_mode_buttons.push_back (Button::Busses);
1169 view_mode_buttons.push_back (Button::Plugin);
1170 view_mode_buttons.push_back (Button::AudioTracks);
1171 view_mode_buttons.push_back (Button::MidiTracks);
1172 view_mode_buttons.push_back (Button::Aux);
1173 view_mode_buttons.push_back (Button::Outputs);
1174 view_mode_buttons.push_back (Button::User);
1178 for (vector<int>::iterator i = view_mode_buttons.begin(); i != view_mode_buttons.end(); ++i) {
1179 map<int,Control*>::iterator x = controls_by_device_independent_id.find (*i);
1181 if (x != controls_by_device_independent_id.end()) {
1182 Button* button = dynamic_cast<Button*> (x->second);
1187 _port->write (button->set_state (onoff));
1193 if (with_helpful_text && !text.empty()) {
1194 display_message_for (text, 1000);
1199 Surface::say_hello ()
1201 /* wakeup for Mackie Control */
1202 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1203 _port->write (wakeup);
1204 wakeup[4] = 0x15; /* wakup Mackie XT */
1205 _port->write (wakeup);
1206 wakeup[4] = 0x10; /* wakeup Logic Control */
1207 _port->write (wakeup);
1208 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1209 _port->write (wakeup);
1213 Surface::next_jog_mode ()
1218 Surface::set_jog_mode (JogWheel::Mode)
1223 Surface::stripable_is_locked_to_strip (boost::shared_ptr<Stripable> stripable) const
1225 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1226 if ((*s)->stripable() == stripable && (*s)->locked()) {
1234 Surface::stripable_is_mapped (boost::shared_ptr<Stripable> stripable) const
1236 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1237 if ((*s)->stripable() == stripable) {
1246 Surface::notify_metering_state_changed()
1248 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1249 (*s)->notify_metering_state_changed ();
1257 /* reset msg for Mackie Control */
1268 Surface::toggle_backlight ()
1271 int onoff = random() %2;
1273 msg << sysex_hdr ();
1275 msg << (onoff ? 0x1 : 0x0);
1282 Surface::recalibrate_faders ()
1286 msg << sysex_hdr ();
1295 Surface::set_touch_sensitivity (int sensitivity)
1297 /* NOTE: assumed called from GUI code, hence sleep() */
1299 /* sensitivity already clamped by caller */
1301 if( !is_qcon ) { // Qcon doesn't support fader sensitivity
1305 msg << sysex_hdr ();
1307 msg << 0xff; /* overwritten for each fader below */
1308 msg << (sensitivity & 0x7f);
1311 for (int fader = 0; fader < 9; ++fader) {
1320 Surface::hui_heartbeat ()
1326 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1331 Surface::connected ()
1333 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1337 if (_mcp.device_info().no_handshake()) {
1343 Surface::display_line (string const& msg, int line_num)
1345 MidiByteArray midi_msg;
1346 midi_msg << sysex_hdr ();
1348 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1350 * correspond to line
1355 midi_msg.insert (midi_msg.end(), 55, ' ');
1359 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1360 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1361 string::size_type len = ascii.length();
1364 midi_msg << ascii.substr (0, 55);
1368 for (string::size_type i = len; i < 55; ++i) {
1374 midi_msg << MIDI::eox;
1379 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1381 * @param msg is assumed to be UTF-8 encoded, and will be converted
1382 * to ASCII with an underscore as fallback character before being
1383 * sent to the device.
1386 Surface::display_message_for (string const& msg, uint64_t msecs)
1388 string::size_type newline;
1390 if ((newline = msg.find ('\n')) == string::npos) {
1392 _port->write (display_line (msg, 0));
1393 _port->write (display_line (string(), 1));
1395 } else if (newline == 0) {
1397 _port->write (display_line (string(), 0));
1398 _port->write (display_line (msg.substr (1), 1));
1402 string first_line = msg.substr (0, newline-1);
1403 string second_line = msg.substr (newline+1);
1405 _port->write (display_line (first_line, 0));
1406 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1409 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1410 (*s)->block_screen_display_for (msecs);