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 "midi++/port.h"
30 #include "ardour/audioengine.h"
31 #include "ardour/automation_control.h"
32 #include "ardour/debug.h"
33 #include "ardour/route.h"
34 #include "ardour/panner.h"
35 #include "ardour/panner_shell.h"
36 #include "ardour/rc_configuration.h"
37 #include "ardour/session.h"
38 #include "ardour/utils.h"
40 #include <gtkmm2ext/gui_thread.h>
42 #include "control_group.h"
43 #include "surface_port.h"
46 #include "mackie_control_protocol.h"
47 #include "jog_wheel.h"
59 #ifdef PLATFORM_WINDOWS
60 #define random() rand()
67 using ARDOUR::Pannable;
68 using ARDOUR::AutomationControl;
69 using namespace ArdourSurface;
70 using namespace Mackie;
72 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
74 // The MCU sysex header.4th byte Will be overwritten
75 // when we get an incoming sysex that identifies
77 static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
79 // The MCU extender sysex header.4th byte Will be overwritten
80 // when we get an incoming sysex that identifies
82 static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x15);
84 static MidiByteArray empty_midi_byte_array;
86 Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype)
95 , _last_master_gain_written (-0.0f)
96 , connection_state (0)
99 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
102 _port = new SurfacePort (*this);
104 throw failed_constructor ();
107 /* only the first Surface object has global controls */
108 /* lets use master_position instead */
109 uint32_t mp = _mcp.device_info().master_position();
111 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
112 if (_mcp.device_info().has_global_controls()) {
114 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
117 if (_mcp.device_info().has_master_fader()) {
119 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
123 uint32_t n = _mcp.device_info().strip_cnt();
127 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
130 if (_mcp.device_info().uses_ipmidi()) {
131 /* ipMIDI port already exists, we can just assume that we're
134 * If the user still hasn't connected the ipMIDI surface and/or
135 * turned it on, then they have to press "Discover Mackie
136 * Devices" in the GUI at the right time.
139 connection_state |= (InputConnected|OutputConnected);
143 connect_to_signals ();
145 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
150 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
153 g_source_destroy (input_source);
157 // delete groups (strips)
158 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
162 // delete controls (global buttons, master fader etc)
163 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
170 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
174 Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
180 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
181 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
183 if (ni == name1 || ni == name2) {
185 connection_state |= InputConnected;
187 connection_state &= ~InputConnected;
189 } else if (no == name1 || no == name2) {
191 connection_state |= OutputConnected;
193 connection_state &= ~OutputConnected;
200 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
202 /* this will send a device query message, which should
203 result in a response that will kick off device type
204 discovery and activation of the surface(s).
206 The intended order of events is:
208 - each surface sends a device query message
209 - devices respond with either MCP or LCP response (sysex in both
211 - sysex message causes Surface::turn_it_on() which tells the
212 MCP object that the surface is ready, and sets up strip
213 displays and binds faders and buttons for that surface
215 In the case of LCP, where this is a handshake process that could
216 fail, the response process to the initial sysex after a device query
217 will mark the surface inactive, which won't shut anything down
218 but will stop any writes to the device.
220 Note: there are no known cases of the handshake process failing.
222 We actually can't initiate this in this callback, so we have
223 to queue it with the MCP event loop.
226 /* XXX this is a horrible hack. Without a short sleep here,
227 something prevents the device wakeup messages from being
228 sent and/or the responses from being received.
235 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
239 return true; /* connection status changed */
245 XMLNode* node = new XMLNode (X_("Surface"));
246 node->add_property (X_("name"), _name);
247 node->add_child_nocopy (_port->get_state());
252 Surface::set_state (const XMLNode& node, int version)
254 /* Look for a node named after this surface */
256 XMLNodeList const& children = node.children();
259 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
260 XMLProperty const* prop = (*c)->property (X_("name"));
262 if (prop->value() == _name) {
273 if (_mcp.session_load ()) {
274 XMLNode* portnode = mynode->child (X_("Port"));
276 if (_port->set_state (*portnode, version)) {
286 Surface::sysex_hdr() const
289 case mcu: return mackie_sysex_hdr;
290 case ext: return mackie_sysex_hdr_xt;
292 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
293 return mackie_sysex_hdr;
296 static GlobalControlDefinition mackie_global_controls[] = {
297 { "external", Pot::External, Pot::factory, "none" },
298 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
299 { "timecode", Led::Timecode, Led::factory, "none" },
300 { "beats", Led::Beats, Led::factory, "none" },
301 { "solo", Led::RudeSolo, Led::factory, "none" },
302 { "relay_click", Led::RelayClick, Led::factory, "none" },
303 { "", 0, Led::factory, "" }
307 Surface::init_controls()
311 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
312 groups["assignment"] = new Group ("assignment");
313 groups["automation"] = new Group ("automation");
314 groups["bank"] = new Group ("bank");
315 groups["cursor"] = new Group ("cursor");
316 groups["display"] = new Group ("display");
317 groups["function select"] = new Group ("function select");
318 groups["global view"] = new Group ("global view");
319 groups["master"] = new Group ("master");
320 groups["modifiers"] = new Group ("modifiers");
321 groups["none"] = new Group ("none");
322 groups["transport"] = new Group ("transport");
323 groups["user"] = new Group ("user");
324 groups["utilities"] = new Group ("utilities");
326 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
327 if (_mcp.device_info().has_jog_wheel()) {
328 _jog_wheel = new Mackie::JogWheel (_mcp);
331 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
332 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
333 group = groups[mackie_global_controls[n].group_name];
334 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
335 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
338 /* add global buttons */
339 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
340 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
342 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
343 group = groups[b->second.group];
344 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
349 Surface::init_strips (uint32_t n)
351 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
353 for (uint32_t i = 0; i < n; ++i) {
357 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
359 Strip* strip = new Strip (*this, name, i, strip_buttons);
361 groups[name] = strip;
362 strips.push_back (strip);
367 Surface::setup_master ()
369 boost::shared_ptr<Route> m;
371 if ((m = _mcp.get_session().monitor_out()) == 0) {
372 m = _mcp.get_session().master_out();
379 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *groups["master"]));
381 _master_fader->set_control (m->gain_control());
382 m->gain_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
384 Groups::iterator group_it;
385 group_it = groups.find("master");
387 DeviceInfo device_info = _mcp.device_info();
388 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
389 Button* bb = dynamic_cast<Button*> (Button::factory (
391 Button::MasterFaderTouch,
396 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
397 number(), Button::MasterFaderTouch, bb->id()));
401 Surface::master_gain_changed ()
403 if (!_master_fader) {
407 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
412 float normalized_position = ac->internal_to_interface (ac->get_value());
413 if (normalized_position == _last_master_gain_written) {
417 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
419 _port->write (_master_fader->set_position (normalized_position));
420 _last_master_gain_written = normalized_position;
424 Surface::scaled_delta (float delta, float current_speed)
426 /* XXX needs work before use */
427 const float sign = delta < 0.0 ? -1.0 : 1.0;
429 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
433 Surface::display_bank_start (uint32_t current_bank)
435 if (current_bank == 0) {
436 // send Ar. to 2-char display on the master
437 show_two_char_display ("Ar", "..");
439 // write the current first remote_id to the 2-char display
440 show_two_char_display (current_bank);
445 Surface::blank_jog_ring ()
447 Control* control = controls_by_device_independent_id[Jog::ID];
450 Pot* pot = dynamic_cast<Pot*> (control);
452 _port->write (pot->set (0.0, false, Pot::spread));
458 Surface::scrub_scaling_factor () const
464 Surface::connect_to_signals ()
469 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
470 number(), _port->input_port().name()));
472 MIDI::Parser* p = _port->input_port().parser();
475 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
476 /* V-Pot messages are Controller */
477 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
478 /* Button messages are NoteOn */
479 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
480 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
481 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
482 /* Fader messages are Pitchbend */
484 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
485 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
488 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()));
495 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
497 /* Pitchbend messages are fader position messages. Nothing in the data we get
498 * from the MIDI::Parser conveys the fader ID, which was given by the
499 * channel ID in the status byte.
501 * Instead, we have used bind() to supply the fader-within-strip ID
502 * when we connected to the per-channel pitchbend events.
505 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
506 fader_id, pb, _number, pb/16384.0));
508 if (_mcp.device_info().no_handshake()) {
512 if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
513 /* user is doing a reset to unity gain but device sends a PB
514 * message in the middle of the touch on/off messages. Ignore
520 Fader* fader = faders[fader_id];
523 Strip* strip = dynamic_cast<Strip*> (&fader->group());
524 float pos = pb / 16384.0;
526 strip->handle_fader (*fader, pos);
528 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
530 fader->set_value (pos); // alter master gain
531 _port->write (fader->set_position (pos)); // write back value (required for servo)
534 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
539 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
541 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
543 if (_mcp.device_info().no_handshake()) {
547 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
551 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
555 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
556 Fader* fader = faders[ev->note_number];
558 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
562 Strip* strip = dynamic_cast<Strip*> (&fader->group());
564 if (ev->velocity > 64) {
565 strip->handle_fader_touch (*fader, true);
567 strip->handle_fader_touch (*fader, false);
573 Button* button = buttons[ev->note_number];
577 if (ev->velocity > 64) {
581 Strip* strip = dynamic_cast<Strip*> (&button->group());
584 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
585 strip->index(), button->name(), (ev->velocity > 64)));
586 strip->handle_button (*button, ev->velocity > 64 ? press : release);
589 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
590 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
593 if (ev->velocity <= 64) {
598 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
601 /* button release should reset timer AFTER handler(s) have run */
605 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
607 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
609 if (_mcp.device_info().no_handshake()) {
613 Pot* pot = pots[ev->controller_number];
615 // bit 6 gives the sign
616 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
617 // bits 0..5 give the velocity. we interpret this as "ticks
618 // moved before this message was sent"
619 float ticks = (ev->value & 0x3f);
621 /* euphonix and perhaps other devices send zero
622 when they mean 1, we think.
628 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_CONTROL) {
629 delta = sign * (ticks / (float) 0xff);
631 delta = sign * (ticks / (float) 0x3f);
635 if (ev->controller_number == Jog::ID && _jog_wheel) {
637 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
638 _jog_wheel->jog_event (delta);
641 // add external (pedal?) control here
646 Strip* strip = dynamic_cast<Strip*> (&pot->group());
648 strip->handle_pot (*pot, delta);
653 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
655 MidiByteArray bytes (count, raw_bytes);
657 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
659 if (_mcp.device_info().no_handshake()) {
663 /* always save the device type ID so that our outgoing sysex messages
668 mackie_sysex_hdr[4] = bytes[4];
670 mackie_sysex_hdr_xt[4] = bytes[4];
676 LCP: Connection Challenge
678 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
679 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
680 write_sysex (host_connection_query (bytes));
682 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
687 case 0x03: /* LCP Connection Confirmation */
688 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
689 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
690 write_sysex (host_connection_confirmation (bytes));
695 case 0x04: /* LCP: Confirmation Denied */
696 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
700 error << "MCP: unknown sysex: " << bytes << endmsg;
705 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
708 back_insert_iterator<MidiByteArray> back (l);
709 copy (begin, end, back);
711 MidiByteArray retval;
713 // this is how to calculate the response to the challenge.
714 // from the Logic docs.
715 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
716 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
717 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
718 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
724 Surface::host_connection_query (MidiByteArray & bytes)
726 MidiByteArray response;
728 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
729 /* not a Logic Control device - no response required */
733 // handle host connection query
734 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
736 if (bytes.size() != 18) {
737 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
741 // build and send host connection reply
743 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
744 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
749 Surface::host_connection_confirmation (const MidiByteArray & bytes)
751 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
753 // decode host connection confirmation
754 if (bytes.size() != 14) {
756 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
757 throw MackieControlException (os.str());
760 // send version request
761 return MidiByteArray (2, 0x13, 0x00);
765 Surface::turn_it_on ()
773 _mcp.device_ready ();
775 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
779 update_view_mode_display ();
781 if (_mcp.device_info ().has_global_controls ()) {
782 _mcp.update_global_button (Button::Read, _mcp.metering_active ());
787 Surface::write_sysex (const MidiByteArray & mba)
794 buf << sysex_hdr() << mba << MIDI::eox;
799 Surface::write_sysex (MIDI::byte msg)
802 buf << sysex_hdr() << msg << MIDI::eox;
807 Surface::n_strips (bool with_locked_strips) const
809 if (with_locked_strips) {
810 return strips.size();
815 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
816 if (!(*it)->locked()) {
824 Surface::nth_strip (uint32_t n) const
826 if (n > n_strips()) {
835 if (_mcp.device_info().has_timecode_display ()) {
836 display_timecode (string (10, '0'), string (10, ' '));
839 if (_mcp.device_info().has_two_character_display()) {
840 show_two_char_display (string (2, '0'), string (2, ' '));
843 if (_mcp.device_info().has_master_fader () && _master_fader) {
844 _port->write (_master_fader->zero ());
848 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
856 Surface::zero_controls ()
858 if (!_mcp.device_info().has_global_controls()) {
862 // turn off global buttons and leds
864 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
865 Control & control = **it;
866 if (!control.group().is_strip()) {
867 _port->write (control.zero());
871 // and the led ring for the master strip
874 _last_master_gain_written = 0.0f;
878 Surface::periodic (uint64_t now_usecs)
880 master_gain_changed();
881 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
882 (*s)->periodic (now_usecs);
887 Surface::redisplay (ARDOUR::microseconds_t now)
889 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
890 (*s)->redisplay (now);
895 Surface::write (const MidiByteArray& data)
900 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
905 Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
907 vector<boost::shared_ptr<Route> >::const_iterator r;
908 Strips::iterator s = strips.begin();
910 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 routes\n", routes.size()));
912 for (r = routes.begin(); r != routes.end() && s != strips.end(); ++s) {
914 /* don't try to assign routes to a locked strip. it won't
915 use it anyway, but if we do, then we get out of sync
916 with the proposed mapping.
919 if (!(*s)->locked()) {
920 (*s)->set_route (*r);
925 for (; s != strips.end(); ++s) {
926 (*s)->set_route (boost::shared_ptr<Route>());
933 translate_seven_segment (char achar)
935 achar = toupper (achar);
937 if (achar >= 0x40 && achar <= 0x60) {
939 } else if (achar >= 0x21 && achar <= 0x3f) {
947 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
949 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
953 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
954 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
956 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
957 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
959 _port->write (right);
964 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
967 os << setfill('0') << setw(2) << value % 100;
968 show_two_char_display (os.str());
972 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
974 if (!_active || !_mcp.device_info().has_timecode_display()) {
977 // if there's no change, send nothing, not even sysex header
978 if (timecode == last_timecode) return;
980 // length sanity checking
981 string local_timecode = timecode;
983 // truncate to 10 characters
984 if (local_timecode.length() > 10) {
985 local_timecode = local_timecode.substr (0, 10);
988 // pad to 10 characters
989 while (local_timecode.length() < 10) {
990 local_timecode += " ";
993 // translate characters.
994 // Only the characters that actually changed are sent.
997 for (i = local_timecode.length () - 1; i >= 0; i--) {
999 if (local_timecode[i] == last_timecode[i]) {
1002 MidiByteArray retval (2, 0xb0, position);
1003 retval << translate_seven_segment (local_timecode[i]);
1004 _port->write (retval);
1009 Surface::update_flip_mode_display ()
1011 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1012 (*s)->flip_mode_changed (true);
1017 Surface::update_potmode ()
1019 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1020 (*s)->potmode_changed (false);
1025 Surface::update_view_mode_display ()
1034 switch (_mcp.view_mode()) {
1035 case MackieControlProtocol::Mixer:
1036 show_two_char_display ("Mx");
1039 case MackieControlProtocol::Loop:
1040 show_two_char_display ("LP");
1043 case MackieControlProtocol::AudioTracks:
1044 show_two_char_display ("AT");
1046 case MackieControlProtocol::MidiTracks:
1047 show_two_char_display ("MT");
1055 /* we are attempting to turn a global button/LED on */
1057 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1059 if (x != controls_by_device_independent_id.end()) {
1060 Button* button = dynamic_cast<Button*> (x->second);
1062 _port->write (button->set_state (on));
1067 if (!text.empty()) {
1068 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1069 _port->write ((*s)->display (1, text));
1075 Surface::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& routes)
1077 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1078 (*s)->gui_selection_changed (routes);
1083 Surface::say_hello ()
1085 /* wakeup for Mackie Control */
1086 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1087 _port->write (wakeup);
1088 wakeup[4] = 0x15; /* wakup Mackie XT */
1089 _port->write (wakeup);
1090 wakeup[4] = 0x10; /* wakeup Logic Control */
1091 _port->write (wakeup);
1092 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1093 _port->write (wakeup);
1097 Surface::next_jog_mode ()
1102 Surface::set_jog_mode (JogWheel::Mode)
1107 Surface::route_is_locked_to_strip (boost::shared_ptr<Route> r) const
1109 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1110 if ((*s)->route() == r && (*s)->locked()) {
1118 Surface::notify_metering_state_changed()
1120 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1121 (*s)->notify_metering_state_changed ();
1129 /* reset msg for Mackie Control */
1140 Surface::toggle_backlight ()
1143 int onoff = random() %2;
1145 msg << sysex_hdr ();
1147 msg << (onoff ? 0x1 : 0x0);
1154 Surface::recalibrate_faders ()
1158 msg << sysex_hdr ();
1167 Surface::set_touch_sensitivity (int sensitivity)
1169 /* NOTE: assumed called from GUI code, hence sleep() */
1171 /* sensitivity already clamped by caller */
1176 msg << sysex_hdr ();
1178 msg << 0xff; /* overwritten for each fader below */
1179 msg << (sensitivity & 0x7f);
1182 for (int fader = 0; fader < 9; ++fader) {
1190 Surface::hui_heartbeat ()
1196 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1201 Surface::connected ()
1203 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1207 if (_mcp.device_info().no_handshake()) {
1213 Surface::display_line (string const& msg, int line_num)
1215 MidiByteArray midi_msg;
1216 midi_msg << sysex_hdr ();
1218 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1220 * correspond to line
1225 midi_msg.insert (midi_msg.end(), 55, ' ');
1229 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1230 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1231 string::size_type len = ascii.length();
1234 midi_msg << ascii.substr (0, 55);
1238 for (string::size_type i = len; i < 55; ++i) {
1244 midi_msg << MIDI::eox;
1249 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1251 * @param msg is assumed to be UTF-8 encoded, and will be converted
1252 * to ASCII with an underscore as fallback character before being
1253 * sent to the device.
1256 Surface::display_message_for (string const& msg, uint64_t msecs)
1258 string::size_type newline;
1260 if ((newline = msg.find ('\n')) == string::npos) {
1262 _port->write (display_line (msg, 0));
1263 _port->write (display_line (string(), 1));
1265 } else if (newline == 0) {
1267 _port->write (display_line (string(), 0));
1268 _port->write (display_line (msg.substr (1), 1));
1272 string first_line = msg.substr (0, newline-1);
1273 string second_line = msg.substr (newline+1);
1275 _port->write (display_line (first_line, 0));
1276 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1279 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1280 (*s)->block_screen_display_for (msecs);