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 "control_group.h"
41 #include "surface_port.h"
44 #include "mackie_control_protocol.h"
45 #include "jog_wheel.h"
57 #ifdef PLATFORM_WINDOWS
58 #define random() rand()
65 using ARDOUR::Pannable;
66 using ARDOUR::AutomationControl;
67 using namespace ArdourSurface;
68 using namespace Mackie;
70 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
72 // The MCU sysex header.4th byte Will be overwritten
73 // when we get an incoming sysex that identifies
75 static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
77 // The MCU extender sysex header.4th byte Will be overwritten
78 // when we get an incoming sysex that identifies
80 static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x15);
82 static MidiByteArray empty_midi_byte_array;
84 Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype)
93 , _last_master_gain_written (-0.0f)
94 , connection_state (0)
96 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
99 _port = new SurfacePort (*this);
101 throw failed_constructor ();
104 /* only the first Surface object has global controls */
105 /* lets use master_position instead */
106 uint32_t mp = _mcp.device_info().master_position();
108 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
109 if (_mcp.device_info().has_global_controls()) {
111 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
114 if (_mcp.device_info().has_master_fader()) {
116 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
120 uint32_t n = _mcp.device_info().strip_cnt();
124 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
127 if (!_mcp.device_info().uses_ipmidi()) {
128 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Surface::connection_handler, this, _1, _2, _3, _4, _5), &_mcp);
130 /* ipMIDI port already exists, we can just assume that we're
133 * If the user still hasn't connected the ipMIDI surface and/or
134 * turned it on, then they have to press "Discover Mackie
135 * Devices" in the GUI at the right time.
138 connection_state |= (InputConnected|OutputConnected);
142 connect_to_signals ();
144 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
149 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
154 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
159 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
163 port_connection.disconnect ();
168 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
172 Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
178 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
179 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
181 if (ni == name1 || ni == name2) {
183 connection_state |= InputConnected;
185 connection_state &= ~InputConnected;
187 } else if (no == name1 || no == name2) {
189 connection_state |= OutputConnected;
191 connection_state &= ~OutputConnected;
195 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
197 /* this will send a device query message, which should
198 result in a response that will kick off device type
199 discovery and activation of the surface(s).
201 The intended order of events is:
203 - each surface sends a device query message
204 - devices respond with either MCP or LCP response (sysex in both
206 - sysex message causes Surface::turn_it_on() which tells the
207 MCP object that the surface is ready, and sets up strip
208 displays and binds faders and buttons for that surface
210 In the case of LCP, where this is a handshake process that could
211 fail, the response process to the initial sysex after a device query
212 will mark the surface inactive, which won't shut anything down
213 but will stop any writes to the device.
215 Note: there are no known cases of the handshake process failing.
217 We actually can't initiate this in this callback, so we have
218 to queue it with the MCP event loop.
224 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
233 snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
234 XMLNode* node = new XMLNode (buf);
236 node->add_child_nocopy (_port->get_state());
242 Surface::set_state (const XMLNode& node, int version)
245 snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
246 XMLNode* mynode = node.child (buf);
252 XMLNode* portnode = mynode->child (X_("Port"));
254 if (_port->set_state (*portnode, version)) {
263 Surface::sysex_hdr() const
266 case mcu: return mackie_sysex_hdr;
267 case ext: return mackie_sysex_hdr_xt;
269 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
270 return mackie_sysex_hdr;
273 static GlobalControlDefinition mackie_global_controls[] = {
274 { "external", Pot::External, Pot::factory, "none" },
275 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
276 { "timecode", Led::Timecode, Led::factory, "none" },
277 { "beats", Led::Beats, Led::factory, "none" },
278 { "solo", Led::RudeSolo, Led::factory, "none" },
279 { "relay_click", Led::RelayClick, Led::factory, "none" },
280 { "", 0, Led::factory, "" }
284 Surface::init_controls()
288 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
289 groups["assignment"] = new Group ("assignment");
290 groups["automation"] = new Group ("automation");
291 groups["bank"] = new Group ("bank");
292 groups["cursor"] = new Group ("cursor");
293 groups["display"] = new Group ("display");
294 groups["function select"] = new Group ("function select");
295 groups["global view"] = new Group ("global view");
296 groups["master"] = new Group ("master");
297 groups["modifiers"] = new Group ("modifiers");
298 groups["none"] = new Group ("none");
299 groups["transport"] = new Group ("transport");
300 groups["user"] = new Group ("user");
301 groups["utilities"] = new Group ("utilities");
303 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
304 if (_mcp.device_info().has_jog_wheel()) {
305 _jog_wheel = new Mackie::JogWheel (_mcp);
308 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
309 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
310 group = groups[mackie_global_controls[n].group_name];
311 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
312 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
315 /* add global buttons */
316 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
317 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
319 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
320 group = groups[b->second.group];
321 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
326 Surface::init_strips (uint32_t n)
328 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
330 for (uint32_t i = 0; i < n; ++i) {
334 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
336 Strip* strip = new Strip (*this, name, i, strip_buttons);
338 groups[name] = strip;
339 strips.push_back (strip);
344 Surface::setup_master ()
346 boost::shared_ptr<Route> m;
348 if ((m = _mcp.get_session().monitor_out()) == 0) {
349 m = _mcp.get_session().master_out();
356 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *groups["master"]));
358 _master_fader->set_control (m->gain_control());
359 m->gain_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
361 Groups::iterator group_it;
362 group_it = groups.find("master");
364 DeviceInfo device_info = _mcp.device_info();
365 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
366 Button* bb = dynamic_cast<Button*> (Button::factory (
368 Button::MasterFaderTouch,
373 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
374 number(), Button::MasterFaderTouch, bb->id()));
378 Surface::master_gain_changed ()
380 if (!_master_fader) {
384 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
389 float normalized_position = ac->internal_to_interface (ac->get_value());
390 if (normalized_position == _last_master_gain_written) {
394 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
396 _port->write (_master_fader->set_position (normalized_position));
397 _last_master_gain_written = normalized_position;
401 Surface::scaled_delta (float delta, float current_speed)
403 /* XXX needs work before use */
404 const float sign = delta < 0.0 ? -1.0 : 1.0;
406 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
410 Surface::display_bank_start (uint32_t current_bank)
412 if (current_bank == 0) {
413 // send Ar. to 2-char display on the master
414 show_two_char_display ("Ar", "..");
416 // write the current first remote_id to the 2-char display
417 show_two_char_display (current_bank);
422 Surface::blank_jog_ring ()
424 Control* control = controls_by_device_independent_id[Jog::ID];
427 Pot* pot = dynamic_cast<Pot*> (control);
429 _port->write (pot->set (0.0, false, Pot::spread));
435 Surface::scrub_scaling_factor () const
441 Surface::connect_to_signals ()
446 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
447 number(), _port->input_port().name()));
449 MIDI::Parser* p = _port->input_port().parser();
452 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
453 /* V-Pot messages are Controller */
454 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
455 /* Button messages are NoteOn */
456 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
457 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
458 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
459 /* Fader messages are Pitchbend */
461 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
462 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
465 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()));
472 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
474 /* Pitchbend messages are fader position messages. Nothing in the data we get
475 * from the MIDI::Parser conveys the fader ID, which was given by the
476 * channel ID in the status byte.
478 * Instead, we have used bind() to supply the fader-within-strip ID
479 * when we connected to the per-channel pitchbend events.
482 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
483 fader_id, pb, _number, pb/16384.0));
485 if (_mcp.device_info().no_handshake()) {
489 if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
490 /* user is doing a reset to unity gain but device sends a PB
491 * message in the middle of the touch on/off messages. Ignore
497 Fader* fader = faders[fader_id];
500 Strip* strip = dynamic_cast<Strip*> (&fader->group());
501 float pos = pb / 16384.0;
503 strip->handle_fader (*fader, pos);
505 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
507 fader->set_value (pos); // alter master gain
508 _port->write (fader->set_position (pos)); // write back value (required for servo)
511 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
516 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
518 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
520 if (_mcp.device_info().no_handshake()) {
524 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
528 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
532 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
533 Fader* fader = faders[ev->note_number];
535 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
539 Strip* strip = dynamic_cast<Strip*> (&fader->group());
541 if (ev->velocity > 64) {
542 strip->handle_fader_touch (*fader, true);
544 strip->handle_fader_touch (*fader, false);
550 Button* button = buttons[ev->note_number];
554 if (ev->velocity > 64) {
558 Strip* strip = dynamic_cast<Strip*> (&button->group());
561 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
562 strip->index(), button->name(), (ev->velocity > 64)));
563 strip->handle_button (*button, ev->velocity > 64 ? press : release);
566 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
567 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
570 if (ev->velocity <= 64) {
575 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
578 /* button release should reset timer AFTER handler(s) have run */
582 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
584 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
586 if (_mcp.device_info().no_handshake()) {
590 Pot* pot = pots[ev->controller_number];
592 // bit 6 gives the sign
593 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
594 // bits 0..5 give the velocity. we interpret this as "ticks
595 // moved before this message was sent"
596 float ticks = (ev->value & 0x3f);
598 /* euphonix and perhaps other devices send zero
599 when they mean 1, we think.
605 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_CONTROL) {
606 delta = sign * (ticks / (float) 0xff);
608 delta = sign * (ticks / (float) 0x3f);
612 if (ev->controller_number == Jog::ID && _jog_wheel) {
614 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
615 _jog_wheel->jog_event (delta);
618 // add external (pedal?) control here
623 Strip* strip = dynamic_cast<Strip*> (&pot->group());
625 strip->handle_pot (*pot, delta);
630 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
632 MidiByteArray bytes (count, raw_bytes);
634 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
636 if (_mcp.device_info().no_handshake()) {
640 /* always save the device type ID so that our outgoing sysex messages
645 mackie_sysex_hdr[4] = bytes[4];
647 mackie_sysex_hdr_xt[4] = bytes[4];
653 LCP: Connection Challenge
655 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
656 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
657 write_sysex (host_connection_query (bytes));
659 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
666 case 0x03: /* LCP Connection Confirmation */
667 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
668 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
669 write_sysex (host_connection_confirmation (bytes));
674 case 0x04: /* LCP: Confirmation Denied */
675 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
679 error << "MCP: unknown sysex: " << bytes << endmsg;
684 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
687 back_insert_iterator<MidiByteArray> back (l);
688 copy (begin, end, back);
690 MidiByteArray retval;
692 // this is how to calculate the response to the challenge.
693 // from the Logic docs.
694 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
695 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
696 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
697 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
703 Surface::host_connection_query (MidiByteArray & bytes)
705 MidiByteArray response;
707 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
708 /* not a Logic Control device - no response required */
712 // handle host connection query
713 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
715 if (bytes.size() != 18) {
716 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
720 // build and send host connection reply
722 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
723 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
728 Surface::host_connection_confirmation (const MidiByteArray & bytes)
730 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
732 // decode host connection confirmation
733 if (bytes.size() != 14) {
735 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
736 throw MackieControlException (os.str());
739 // send version request
740 return MidiByteArray (2, 0x13, 0x00);
744 Surface::turn_it_on ()
752 _mcp.device_ready ();
754 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
758 update_view_mode_display ();
760 if (_mcp.device_info ().has_global_controls ()) {
761 _mcp.update_global_button (Button::Read, _mcp.metering_active ());
766 Surface::write_sysex (const MidiByteArray & mba)
773 buf << sysex_hdr() << mba << MIDI::eox;
778 Surface::write_sysex (MIDI::byte msg)
781 buf << sysex_hdr() << msg << MIDI::eox;
786 Surface::n_strips (bool with_locked_strips) const
788 if (with_locked_strips) {
789 return strips.size();
794 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
795 if (!(*it)->locked()) {
803 Surface::nth_strip (uint32_t n) const
805 if (n > n_strips()) {
814 if (_mcp.device_info().has_timecode_display ()) {
815 display_timecode (string (10, '0'), string (10, ' '));
818 if (_mcp.device_info().has_two_character_display()) {
819 show_two_char_display (string (2, '0'), string (2, ' '));
822 if (_mcp.device_info().has_master_fader () && _master_fader) {
823 _port->write (_master_fader->zero ());
827 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
835 Surface::zero_controls ()
837 if (!_mcp.device_info().has_global_controls()) {
841 // turn off global buttons and leds
843 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
844 Control & control = **it;
845 if (!control.group().is_strip()) {
846 _port->write (control.zero());
850 // and the led ring for the master strip
853 _last_master_gain_written = 0.0f;
857 Surface::periodic (uint64_t now_usecs)
859 master_gain_changed();
860 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
861 (*s)->periodic (now_usecs);
866 Surface::redisplay (ARDOUR::microseconds_t now)
868 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
869 (*s)->redisplay (now);
874 Surface::write (const MidiByteArray& data)
879 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
884 Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
886 vector<boost::shared_ptr<Route> >::const_iterator r;
887 Strips::iterator s = strips.begin();
889 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 routes\n", routes.size()));
891 for (r = routes.begin(); r != routes.end() && s != strips.end(); ++s) {
893 /* don't try to assign routes to a locked strip. it won't
894 use it anyway, but if we do, then we get out of sync
895 with the proposed mapping.
898 if (!(*s)->locked()) {
899 (*s)->set_route (*r);
904 for (; s != strips.end(); ++s) {
905 (*s)->set_route (boost::shared_ptr<Route>());
912 translate_seven_segment (char achar)
914 achar = toupper (achar);
916 if (achar >= 0x40 && achar <= 0x60) {
918 } else if (achar >= 0x21 && achar <= 0x3f) {
926 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
928 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
932 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
933 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
935 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
936 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
938 _port->write (right);
943 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
946 os << setfill('0') << setw(2) << value % 100;
947 show_two_char_display (os.str());
951 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
953 if (!_active || !_mcp.device_info().has_timecode_display()) {
956 // if there's no change, send nothing, not even sysex header
957 if (timecode == last_timecode) return;
959 // length sanity checking
960 string local_timecode = timecode;
962 // truncate to 10 characters
963 if (local_timecode.length() > 10) {
964 local_timecode = local_timecode.substr (0, 10);
967 // pad to 10 characters
968 while (local_timecode.length() < 10) {
969 local_timecode += " ";
972 // translate characters.
973 // Only the characters that actually changed are sent.
976 for (i = local_timecode.length () - 1; i >= 0; i--) {
978 if (local_timecode[i] == last_timecode[i]) {
981 MidiByteArray retval (2, 0xb0, position);
982 retval << translate_seven_segment (local_timecode[i]);
983 _port->write (retval);
988 Surface::update_flip_mode_display ()
990 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
991 (*s)->flip_mode_changed (true);
996 Surface::update_potmode ()
998 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
999 (*s)->potmode_changed (false);
1004 Surface::update_view_mode_display ()
1013 switch (_mcp.view_mode()) {
1014 case MackieControlProtocol::Mixer:
1015 show_two_char_display ("Mx");
1018 case MackieControlProtocol::Loop:
1019 show_two_char_display ("LP");
1022 case MackieControlProtocol::AudioTracks:
1023 show_two_char_display ("AT");
1025 case MackieControlProtocol::MidiTracks:
1026 show_two_char_display ("MT");
1034 /* we are attempting to turn a global button/LED on */
1036 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1038 if (x != controls_by_device_independent_id.end()) {
1039 Button* button = dynamic_cast<Button*> (x->second);
1041 _port->write (button->set_state (on));
1046 if (!text.empty()) {
1047 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1048 _port->write ((*s)->display (1, text));
1054 Surface::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& routes)
1056 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1057 (*s)->gui_selection_changed (routes);
1062 Surface::say_hello ()
1064 /* wakeup for Mackie Control */
1065 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1066 _port->write (wakeup);
1067 wakeup[4] = 0x15; /* wakup Mackie XT */
1068 _port->write (wakeup);
1069 wakeup[4] = 0x10; /* wakeup Logic Control */
1070 _port->write (wakeup);
1071 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1072 _port->write (wakeup);
1076 Surface::next_jog_mode ()
1081 Surface::set_jog_mode (JogWheel::Mode)
1086 Surface::route_is_locked_to_strip (boost::shared_ptr<Route> r) const
1088 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1089 if ((*s)->route() == r && (*s)->locked()) {
1097 Surface::notify_metering_state_changed()
1099 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1100 (*s)->notify_metering_state_changed ();
1108 /* reset msg for Mackie Control */
1119 Surface::toggle_backlight ()
1122 int onoff = random() %2;
1124 msg << sysex_hdr ();
1126 msg << (onoff ? 0x1 : 0x0);
1133 Surface::recalibrate_faders ()
1137 msg << sysex_hdr ();
1146 Surface::set_touch_sensitivity (int sensitivity)
1148 /* NOTE: assumed called from GUI code, hence sleep() */
1150 /* sensitivity already clamped by caller */
1155 msg << sysex_hdr ();
1157 msg << 0xff; /* overwritten for each fader below */
1158 msg << (sensitivity & 0x7f);
1161 for (int fader = 0; fader < 9; ++fader) {
1169 Surface::hui_heartbeat ()
1175 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1180 Surface::connected ()
1182 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1186 if (_mcp.device_info().no_handshake()) {
1192 Surface::display_line (string const& msg, int line_num)
1194 MidiByteArray midi_msg;
1195 midi_msg << sysex_hdr ();
1197 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1199 * correspond to line
1204 midi_msg.insert (midi_msg.end(), 55, ' ');
1208 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1209 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1210 string::size_type len = ascii.length();
1213 midi_msg << ascii.substr (0, 55);
1217 for (string::size_type i = len; i < 55; ++i) {
1223 midi_msg << MIDI::eox;
1228 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1230 * @param msg is assumed to be UTF-8 encoded, and will be converted
1231 * to ASCII with an underscore as fallback character before being
1232 * sent to the device.
1235 Surface::display_message_for (string const& msg, uint64_t msecs)
1237 string::size_type newline;
1239 if ((newline = msg.find ('\n')) == string::npos) {
1241 _port->write (display_line (msg, 0));
1242 _port->write (display_line (string(), 1));
1244 } else if (newline == 0) {
1246 _port->write (display_line (string(), 0));
1247 _port->write (display_line (msg.substr (1), 1));
1251 string first_line = msg.substr (0, newline-1);
1252 string second_line = msg.substr (newline+1);
1254 _port->write (display_line (first_line, 0));
1255 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1258 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1259 (*s)->block_screen_display_for (msecs);