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.
236 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
240 return true; /* connection status changed */
247 snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
248 XMLNode* node = new XMLNode (buf);
250 node->add_child_nocopy (_port->get_state());
256 Surface::set_state (const XMLNode& node, int version)
259 snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
260 XMLNode* mynode = node.child (buf);
266 XMLNode* portnode = mynode->child (X_("Port"));
268 if (_port->set_state (*portnode, version)) {
277 Surface::sysex_hdr() const
280 case mcu: return mackie_sysex_hdr;
281 case ext: return mackie_sysex_hdr_xt;
283 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
284 return mackie_sysex_hdr;
287 static GlobalControlDefinition mackie_global_controls[] = {
288 { "external", Pot::External, Pot::factory, "none" },
289 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
290 { "timecode", Led::Timecode, Led::factory, "none" },
291 { "beats", Led::Beats, Led::factory, "none" },
292 { "solo", Led::RudeSolo, Led::factory, "none" },
293 { "relay_click", Led::RelayClick, Led::factory, "none" },
294 { "", 0, Led::factory, "" }
298 Surface::init_controls()
302 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
303 groups["assignment"] = new Group ("assignment");
304 groups["automation"] = new Group ("automation");
305 groups["bank"] = new Group ("bank");
306 groups["cursor"] = new Group ("cursor");
307 groups["display"] = new Group ("display");
308 groups["function select"] = new Group ("function select");
309 groups["global view"] = new Group ("global view");
310 groups["master"] = new Group ("master");
311 groups["modifiers"] = new Group ("modifiers");
312 groups["none"] = new Group ("none");
313 groups["transport"] = new Group ("transport");
314 groups["user"] = new Group ("user");
315 groups["utilities"] = new Group ("utilities");
317 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
318 if (_mcp.device_info().has_jog_wheel()) {
319 _jog_wheel = new Mackie::JogWheel (_mcp);
322 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
323 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
324 group = groups[mackie_global_controls[n].group_name];
325 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
326 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
329 /* add global buttons */
330 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
331 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
333 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
334 group = groups[b->second.group];
335 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
340 Surface::init_strips (uint32_t n)
342 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
344 for (uint32_t i = 0; i < n; ++i) {
348 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
350 Strip* strip = new Strip (*this, name, i, strip_buttons);
352 groups[name] = strip;
353 strips.push_back (strip);
358 Surface::setup_master ()
360 boost::shared_ptr<Route> m;
362 if ((m = _mcp.get_session().monitor_out()) == 0) {
363 m = _mcp.get_session().master_out();
370 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *groups["master"]));
372 _master_fader->set_control (m->gain_control());
373 m->gain_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
375 Groups::iterator group_it;
376 group_it = groups.find("master");
378 DeviceInfo device_info = _mcp.device_info();
379 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
380 Button* bb = dynamic_cast<Button*> (Button::factory (
382 Button::MasterFaderTouch,
387 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
388 number(), Button::MasterFaderTouch, bb->id()));
392 Surface::master_gain_changed ()
394 if (!_master_fader) {
398 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
403 float normalized_position = ac->internal_to_interface (ac->get_value());
404 if (normalized_position == _last_master_gain_written) {
408 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
410 _port->write (_master_fader->set_position (normalized_position));
411 _last_master_gain_written = normalized_position;
415 Surface::scaled_delta (float delta, float current_speed)
417 /* XXX needs work before use */
418 const float sign = delta < 0.0 ? -1.0 : 1.0;
420 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
424 Surface::display_bank_start (uint32_t current_bank)
426 if (current_bank == 0) {
427 // send Ar. to 2-char display on the master
428 show_two_char_display ("Ar", "..");
430 // write the current first remote_id to the 2-char display
431 show_two_char_display (current_bank);
436 Surface::blank_jog_ring ()
438 Control* control = controls_by_device_independent_id[Jog::ID];
441 Pot* pot = dynamic_cast<Pot*> (control);
443 _port->write (pot->set (0.0, false, Pot::spread));
449 Surface::scrub_scaling_factor () const
455 Surface::connect_to_signals ()
460 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
461 number(), _port->input_port().name()));
463 MIDI::Parser* p = _port->input_port().parser();
466 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
467 /* V-Pot messages are Controller */
468 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
469 /* Button messages are NoteOn */
470 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
471 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
472 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
473 /* Fader messages are Pitchbend */
475 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
476 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
479 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()));
486 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
488 /* Pitchbend messages are fader position messages. Nothing in the data we get
489 * from the MIDI::Parser conveys the fader ID, which was given by the
490 * channel ID in the status byte.
492 * Instead, we have used bind() to supply the fader-within-strip ID
493 * when we connected to the per-channel pitchbend events.
496 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
497 fader_id, pb, _number, pb/16384.0));
499 if (_mcp.device_info().no_handshake()) {
503 if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
504 /* user is doing a reset to unity gain but device sends a PB
505 * message in the middle of the touch on/off messages. Ignore
511 Fader* fader = faders[fader_id];
514 Strip* strip = dynamic_cast<Strip*> (&fader->group());
515 float pos = pb / 16384.0;
517 strip->handle_fader (*fader, pos);
519 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
521 fader->set_value (pos); // alter master gain
522 _port->write (fader->set_position (pos)); // write back value (required for servo)
525 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
530 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
532 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
534 if (_mcp.device_info().no_handshake()) {
538 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
542 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
546 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
547 Fader* fader = faders[ev->note_number];
549 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
553 Strip* strip = dynamic_cast<Strip*> (&fader->group());
555 if (ev->velocity > 64) {
556 strip->handle_fader_touch (*fader, true);
558 strip->handle_fader_touch (*fader, false);
564 Button* button = buttons[ev->note_number];
568 if (ev->velocity > 64) {
572 Strip* strip = dynamic_cast<Strip*> (&button->group());
575 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
576 strip->index(), button->name(), (ev->velocity > 64)));
577 strip->handle_button (*button, ev->velocity > 64 ? press : release);
580 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
581 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
584 if (ev->velocity <= 64) {
589 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
592 /* button release should reset timer AFTER handler(s) have run */
596 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
598 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
600 if (_mcp.device_info().no_handshake()) {
604 Pot* pot = pots[ev->controller_number];
606 // bit 6 gives the sign
607 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
608 // bits 0..5 give the velocity. we interpret this as "ticks
609 // moved before this message was sent"
610 float ticks = (ev->value & 0x3f);
612 /* euphonix and perhaps other devices send zero
613 when they mean 1, we think.
619 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_CONTROL) {
620 delta = sign * (ticks / (float) 0xff);
622 delta = sign * (ticks / (float) 0x3f);
626 if (ev->controller_number == Jog::ID && _jog_wheel) {
628 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
629 _jog_wheel->jog_event (delta);
632 // add external (pedal?) control here
637 Strip* strip = dynamic_cast<Strip*> (&pot->group());
639 strip->handle_pot (*pot, delta);
644 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
646 MidiByteArray bytes (count, raw_bytes);
648 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
650 if (_mcp.device_info().no_handshake()) {
654 /* always save the device type ID so that our outgoing sysex messages
659 mackie_sysex_hdr[4] = bytes[4];
661 mackie_sysex_hdr_xt[4] = bytes[4];
667 LCP: Connection Challenge
669 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
670 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
671 write_sysex (host_connection_query (bytes));
673 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
678 case 0x03: /* LCP Connection Confirmation */
679 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
680 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
681 write_sysex (host_connection_confirmation (bytes));
686 case 0x04: /* LCP: Confirmation Denied */
687 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
691 error << "MCP: unknown sysex: " << bytes << endmsg;
696 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
699 back_insert_iterator<MidiByteArray> back (l);
700 copy (begin, end, back);
702 MidiByteArray retval;
704 // this is how to calculate the response to the challenge.
705 // from the Logic docs.
706 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
707 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
708 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
709 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
715 Surface::host_connection_query (MidiByteArray & bytes)
717 MidiByteArray response;
719 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
720 /* not a Logic Control device - no response required */
724 // handle host connection query
725 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
727 if (bytes.size() != 18) {
728 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
732 // build and send host connection reply
734 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
735 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
740 Surface::host_connection_confirmation (const MidiByteArray & bytes)
742 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
744 // decode host connection confirmation
745 if (bytes.size() != 14) {
747 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
748 throw MackieControlException (os.str());
751 // send version request
752 return MidiByteArray (2, 0x13, 0x00);
756 Surface::turn_it_on ()
764 _mcp.device_ready ();
766 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
770 update_view_mode_display ();
772 if (_mcp.device_info ().has_global_controls ()) {
773 _mcp.update_global_button (Button::Read, _mcp.metering_active ());
778 Surface::write_sysex (const MidiByteArray & mba)
785 buf << sysex_hdr() << mba << MIDI::eox;
790 Surface::write_sysex (MIDI::byte msg)
793 buf << sysex_hdr() << msg << MIDI::eox;
798 Surface::n_strips (bool with_locked_strips) const
800 if (with_locked_strips) {
801 return strips.size();
806 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
807 if (!(*it)->locked()) {
815 Surface::nth_strip (uint32_t n) const
817 if (n > n_strips()) {
826 if (_mcp.device_info().has_timecode_display ()) {
827 display_timecode (string (10, '0'), string (10, ' '));
830 if (_mcp.device_info().has_two_character_display()) {
831 show_two_char_display (string (2, '0'), string (2, ' '));
834 if (_mcp.device_info().has_master_fader () && _master_fader) {
835 _port->write (_master_fader->zero ());
839 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
847 Surface::zero_controls ()
849 if (!_mcp.device_info().has_global_controls()) {
853 // turn off global buttons and leds
855 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
856 Control & control = **it;
857 if (!control.group().is_strip()) {
858 _port->write (control.zero());
862 // and the led ring for the master strip
865 _last_master_gain_written = 0.0f;
869 Surface::periodic (uint64_t now_usecs)
871 master_gain_changed();
872 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
873 (*s)->periodic (now_usecs);
878 Surface::redisplay (ARDOUR::microseconds_t now)
880 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
881 (*s)->redisplay (now);
886 Surface::write (const MidiByteArray& data)
891 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
896 Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
898 vector<boost::shared_ptr<Route> >::const_iterator r;
899 Strips::iterator s = strips.begin();
901 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 routes\n", routes.size()));
903 for (r = routes.begin(); r != routes.end() && s != strips.end(); ++s) {
905 /* don't try to assign routes to a locked strip. it won't
906 use it anyway, but if we do, then we get out of sync
907 with the proposed mapping.
910 if (!(*s)->locked()) {
911 (*s)->set_route (*r);
916 for (; s != strips.end(); ++s) {
917 (*s)->set_route (boost::shared_ptr<Route>());
924 translate_seven_segment (char achar)
926 achar = toupper (achar);
928 if (achar >= 0x40 && achar <= 0x60) {
930 } else if (achar >= 0x21 && achar <= 0x3f) {
938 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
940 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
944 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
945 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
947 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
948 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
950 _port->write (right);
955 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
958 os << setfill('0') << setw(2) << value % 100;
959 show_two_char_display (os.str());
963 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
965 if (!_active || !_mcp.device_info().has_timecode_display()) {
968 // if there's no change, send nothing, not even sysex header
969 if (timecode == last_timecode) return;
971 // length sanity checking
972 string local_timecode = timecode;
974 // truncate to 10 characters
975 if (local_timecode.length() > 10) {
976 local_timecode = local_timecode.substr (0, 10);
979 // pad to 10 characters
980 while (local_timecode.length() < 10) {
981 local_timecode += " ";
984 // translate characters.
985 // Only the characters that actually changed are sent.
988 for (i = local_timecode.length () - 1; i >= 0; i--) {
990 if (local_timecode[i] == last_timecode[i]) {
993 MidiByteArray retval (2, 0xb0, position);
994 retval << translate_seven_segment (local_timecode[i]);
995 _port->write (retval);
1000 Surface::update_flip_mode_display ()
1002 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1003 (*s)->flip_mode_changed (true);
1008 Surface::update_potmode ()
1010 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1011 (*s)->potmode_changed (false);
1016 Surface::update_view_mode_display ()
1025 switch (_mcp.view_mode()) {
1026 case MackieControlProtocol::Mixer:
1027 show_two_char_display ("Mx");
1030 case MackieControlProtocol::Loop:
1031 show_two_char_display ("LP");
1034 case MackieControlProtocol::AudioTracks:
1035 show_two_char_display ("AT");
1037 case MackieControlProtocol::MidiTracks:
1038 show_two_char_display ("MT");
1046 /* we are attempting to turn a global button/LED on */
1048 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1050 if (x != controls_by_device_independent_id.end()) {
1051 Button* button = dynamic_cast<Button*> (x->second);
1053 _port->write (button->set_state (on));
1058 if (!text.empty()) {
1059 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1060 _port->write ((*s)->display (1, text));
1066 Surface::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& routes)
1068 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1069 (*s)->gui_selection_changed (routes);
1074 Surface::say_hello ()
1076 /* wakeup for Mackie Control */
1077 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1078 _port->write (wakeup);
1079 wakeup[4] = 0x15; /* wakup Mackie XT */
1080 _port->write (wakeup);
1081 wakeup[4] = 0x10; /* wakeup Logic Control */
1082 _port->write (wakeup);
1083 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1084 _port->write (wakeup);
1088 Surface::next_jog_mode ()
1093 Surface::set_jog_mode (JogWheel::Mode)
1098 Surface::route_is_locked_to_strip (boost::shared_ptr<Route> r) const
1100 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1101 if ((*s)->route() == r && (*s)->locked()) {
1109 Surface::notify_metering_state_changed()
1111 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1112 (*s)->notify_metering_state_changed ();
1120 /* reset msg for Mackie Control */
1131 Surface::toggle_backlight ()
1134 int onoff = random() %2;
1136 msg << sysex_hdr ();
1138 msg << (onoff ? 0x1 : 0x0);
1145 Surface::recalibrate_faders ()
1149 msg << sysex_hdr ();
1158 Surface::set_touch_sensitivity (int sensitivity)
1160 /* NOTE: assumed called from GUI code, hence sleep() */
1162 /* sensitivity already clamped by caller */
1167 msg << sysex_hdr ();
1169 msg << 0xff; /* overwritten for each fader below */
1170 msg << (sensitivity & 0x7f);
1173 for (int fader = 0; fader < 9; ++fader) {
1181 Surface::hui_heartbeat ()
1187 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1192 Surface::connected ()
1194 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1198 if (_mcp.device_info().no_handshake()) {
1204 Surface::display_line (string const& msg, int line_num)
1206 MidiByteArray midi_msg;
1207 midi_msg << sysex_hdr ();
1209 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1211 * correspond to line
1216 midi_msg.insert (midi_msg.end(), 55, ' ');
1220 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1221 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1222 string::size_type len = ascii.length();
1225 midi_msg << ascii.substr (0, 55);
1229 for (string::size_type i = len; i < 55; ++i) {
1235 midi_msg << MIDI::eox;
1240 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1242 * @param msg is assumed to be UTF-8 encoded, and will be converted
1243 * to ASCII with an underscore as fallback character before being
1244 * sent to the device.
1247 Surface::display_message_for (string const& msg, uint64_t msecs)
1249 string::size_type newline;
1251 if ((newline = msg.find ('\n')) == string::npos) {
1253 _port->write (display_line (msg, 0));
1254 _port->write (display_line (string(), 1));
1256 } else if (newline == 0) {
1258 _port->write (display_line (string(), 0));
1259 _port->write (display_line (msg.substr (1), 1));
1263 string first_line = msg.substr (0, newline-1);
1264 string second_line = msg.substr (newline+1);
1266 _port->write (display_line (first_line, 0));
1267 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1270 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1271 (*s)->block_screen_display_for (msecs);