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);
87 static MidiByteArray empty_midi_byte_array;
89 Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype)
98 , _last_master_gain_written (-0.0f)
99 , connection_state (0)
102 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
105 _port = new SurfacePort (*this);
107 throw failed_constructor ();
110 /* only the first Surface object has global controls */
111 /* lets use master_position instead */
112 uint32_t mp = _mcp.device_info().master_position();
114 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
115 if (_mcp.device_info().has_global_controls()) {
117 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
120 if (_mcp.device_info().has_master_fader()) {
122 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
126 uint32_t n = _mcp.device_info().strip_cnt();
130 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
133 if (_mcp.device_info().uses_ipmidi()) {
134 /* ipMIDI port already exists, we can just assume that we're
137 * If the user still hasn't connected the ipMIDI surface and/or
138 * turned it on, then they have to press "Discover Mackie
139 * Devices" in the GUI at the right time.
142 connection_state |= (InputConnected|OutputConnected);
146 connect_to_signals ();
148 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
153 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
156 g_source_destroy (input_source);
160 // delete groups (strips)
161 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
165 // delete controls (global buttons, master fader etc)
166 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
172 // the ports take time to release and we may be rebuilding right away
173 // in the case of changing devices.
175 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
179 Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
185 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
186 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
188 if (ni == name1 || ni == name2) {
190 connection_state |= InputConnected;
192 connection_state &= ~InputConnected;
194 } else if (no == name1 || no == name2) {
196 connection_state |= OutputConnected;
198 connection_state &= ~OutputConnected;
205 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
207 /* this will send a device query message, which should
208 result in a response that will kick off device type
209 discovery and activation of the surface(s).
211 The intended order of events is:
213 - each surface sends a device query message
214 - devices respond with either MCP or LCP response (sysex in both
216 - sysex message causes Surface::turn_it_on() which tells the
217 MCP object that the surface is ready, and sets up strip
218 displays and binds faders and buttons for that surface
220 In the case of LCP, where this is a handshake process that could
221 fail, the response process to the initial sysex after a device query
222 will mark the surface inactive, which won't shut anything down
223 but will stop any writes to the device.
225 Note: there are no known cases of the handshake process failing.
227 We actually can't initiate this in this callback, so we have
228 to queue it with the MCP event loop.
231 /* XXX this is a horrible hack. Without a short sleep here,
232 something prevents the device wakeup messages from being
233 sent and/or the responses from being received.
240 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
244 return true; /* connection status changed */
250 XMLNode* node = new XMLNode (X_("Surface"));
251 node->set_property (X_("name"), _name);
252 node->add_child_nocopy (_port->get_state());
257 Surface::set_state (const XMLNode& node, int version)
259 /* Look for a node named after the device we're part of */
261 XMLNodeList const& children = node.children();
264 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
266 if ((*c)->get_property (X_("name"), name) && name == _name) {
276 XMLNode* portnode = mynode->child (X_("Port"));
278 if (_port->set_state (*portnode, version)) {
287 Surface::sysex_hdr() const
290 case mcu: return mackie_sysex_hdr;
291 case ext: return mackie_sysex_hdr_xt;
293 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
294 return mackie_sysex_hdr;
297 static GlobalControlDefinition mackie_global_controls[] = {
298 { "external", Pot::External, Pot::factory, "none" },
299 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
300 { "timecode", Led::Timecode, Led::factory, "none" },
301 { "beats", Led::Beats, Led::factory, "none" },
302 { "solo", Led::RudeSolo, Led::factory, "none" },
303 { "relay_click", Led::RelayClick, Led::factory, "none" },
304 { "", 0, Led::factory, "" }
308 Surface::init_controls()
312 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
313 groups["assignment"] = new Group ("assignment");
314 groups["automation"] = new Group ("automation");
315 groups["bank"] = new Group ("bank");
316 groups["cursor"] = new Group ("cursor");
317 groups["display"] = new Group ("display");
318 groups["function select"] = new Group ("function select");
319 groups["global view"] = new Group ("global view");
320 groups["master"] = new Group ("master");
321 groups["modifiers"] = new Group ("modifiers");
322 groups["none"] = new Group ("none");
323 groups["transport"] = new Group ("transport");
324 groups["user"] = new Group ("user");
325 groups["utilities"] = new Group ("utilities");
327 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
328 if (_mcp.device_info().has_jog_wheel()) {
329 _jog_wheel = new Mackie::JogWheel (_mcp);
332 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
333 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
334 group = groups[mackie_global_controls[n].group_name];
335 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
336 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
339 /* add global buttons */
340 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
341 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
343 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
344 group = groups[b->second.group];
345 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
350 Surface::init_strips (uint32_t n)
352 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
354 for (uint32_t i = 0; i < n; ++i) {
358 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
360 Strip* strip = new Strip (*this, name, i, strip_buttons);
362 groups[name] = strip;
363 strips.push_back (strip);
368 Surface::master_monitor_may_have_changed ()
370 if (_number == _mcp.device_info().master_position()) {
376 Surface::setup_master ()
378 boost::shared_ptr<Stripable> m;
380 if ((m = _mcp.get_session().monitor_out()) == 0) {
381 m = _mcp.get_session().master_out();
386 _master_fader->set_control (boost::shared_ptr<AutomationControl>());
388 master_connection.disconnect ();
392 if (!_master_fader) {
393 Groups::iterator group_it;
395 group_it = groups.find("master");
397 if (group_it == groups.end()) {
398 groups["master"] = master_group = new Group ("master");
400 master_group = group_it->second;
403 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *master_group));
405 DeviceInfo device_info = _mcp.device_info();
406 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
407 Button* bb = dynamic_cast<Button*> (Button::factory (
409 Button::MasterFaderTouch,
415 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
416 number(), Button::MasterFaderTouch, bb->id()));
418 master_connection.disconnect ();
421 _master_fader->set_control (m->gain_control());
422 m->gain_control()->Changed.connect (master_connection, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
423 _last_master_gain_written = FLT_MAX; /* some essentially impossible value */
424 master_gain_changed ();
428 Surface::master_gain_changed ()
430 if (!_master_fader) {
434 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
439 float normalized_position = ac->internal_to_interface (ac->get_value());
440 if (normalized_position == _last_master_gain_written) {
444 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
446 _port->write (_master_fader->set_position (normalized_position));
447 _last_master_gain_written = normalized_position;
451 Surface::scaled_delta (float delta, float current_speed)
453 /* XXX needs work before use */
454 const float sign = delta < 0.0 ? -1.0 : 1.0;
456 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
460 Surface::display_bank_start (uint32_t current_bank)
462 if (current_bank == 0) {
463 // send Ar. to 2-char display on the master
464 show_two_char_display ("Ar", "..");
466 // write the current first remote_id to the 2-char display
467 show_two_char_display (current_bank);
472 Surface::blank_jog_ring ()
474 Control* control = controls_by_device_independent_id[Jog::ID];
477 Pot* pot = dynamic_cast<Pot*> (control);
479 _port->write (pot->set (0.0, false, Pot::spread));
485 Surface::scrub_scaling_factor () const
491 Surface::connect_to_signals ()
496 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
497 number(), _port->input_port().name()));
499 MIDI::Parser* p = _port->input_port().parser();
502 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
503 /* V-Pot messages are Controller */
504 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
505 /* Button messages are NoteOn */
506 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
507 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
508 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
509 /* Fader messages are Pitchbend */
511 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
512 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
515 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()));
522 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
524 /* Pitchbend messages are fader position messages. Nothing in the data we get
525 * from the MIDI::Parser conveys the fader ID, which was given by the
526 * channel ID in the status byte.
528 * Instead, we have used bind() to supply the fader-within-strip ID
529 * when we connected to the per-channel pitchbend events.
532 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
533 fader_id, pb, _number, pb/16384.0));
535 if (_mcp.device_info().no_handshake()) {
539 Fader* fader = faders[fader_id];
542 Strip* strip = dynamic_cast<Strip*> (&fader->group());
543 float pos = pb / 16384.0;
545 strip->handle_fader (*fader, pos);
547 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
549 fader->set_value (pos); // alter master gain
550 _port->write (fader->set_position (pos)); // write back value (required for servo)
553 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
558 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
560 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
562 if (_mcp.device_info().no_handshake()) {
566 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
570 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
574 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
575 Fader* fader = faders[ev->note_number];
577 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
581 Strip* strip = dynamic_cast<Strip*> (&fader->group());
583 if (ev->velocity > 64) {
584 strip->handle_fader_touch (*fader, true);
586 strip->handle_fader_touch (*fader, false);
592 Button* button = buttons[ev->note_number];
596 if (ev->velocity > 64) {
600 Strip* strip = dynamic_cast<Strip*> (&button->group());
603 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
604 strip->index(), button->name(), (ev->velocity > 64)));
605 strip->handle_button (*button, ev->velocity > 64 ? press : release);
608 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
609 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
612 if (ev->velocity <= 64) {
617 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
620 /* button release should reset timer AFTER handler(s) have run */
624 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
626 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
628 if (_mcp.device_info().no_handshake()) {
632 Pot* pot = pots[ev->controller_number];
634 // bit 6 gives the sign
635 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
636 // bits 0..5 give the velocity. we interpret this as "ticks
637 // moved before this message was sent"
638 float ticks = (ev->value & 0x3f);
640 /* euphonix and perhaps other devices send zero
641 when they mean 1, we think.
647 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_SHIFT) {
648 delta = sign * (ticks / (float) 0xff);
650 delta = sign * (ticks / (float) 0x3f);
654 if (ev->controller_number == Jog::ID && _jog_wheel) {
656 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
657 _jog_wheel->jog_event (delta);
660 // add external (pedal?) control here
665 Strip* strip = dynamic_cast<Strip*> (&pot->group());
667 strip->handle_pot (*pot, delta);
672 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
674 MidiByteArray bytes (count, raw_bytes);
676 if (_mcp.device_info().no_handshake()) {
680 /* always save the device type ID so that our outgoing sysex messages
685 mackie_sysex_hdr[4] = bytes[4];
687 mackie_sysex_hdr_xt[4] = bytes[4];
693 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
696 LCP: Connection Challenge
698 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
699 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
700 write_sysex (host_connection_query (bytes));
703 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
711 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
713 /* Behringer X-Touch Compact: Device Ready
715 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Behringer X-Touch Compact ready, current status = %1\n", _active));
719 case 0x03: /* LCP Connection Confirmation */
720 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
721 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
722 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
723 write_sysex (host_connection_confirmation (bytes));
728 case 0x04: /* LCP: Confirmation Denied */
729 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
730 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
735 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
736 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("unknown device ID byte %1", (int) bytes[5]));
737 error << "MCP: unknown sysex: " << bytes << endmsg;
742 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
745 back_insert_iterator<MidiByteArray> back (l);
746 copy (begin, end, back);
748 MidiByteArray retval;
750 // this is how to calculate the response to the challenge.
751 // from the Logic docs.
752 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
753 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
754 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
755 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
761 Surface::host_connection_query (MidiByteArray & bytes)
763 MidiByteArray response;
765 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
766 /* not a Logic Control device - no response required */
770 // handle host connection query
771 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
773 if (bytes.size() != 18) {
774 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
778 // build and send host connection reply
780 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
781 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
786 Surface::host_connection_confirmation (const MidiByteArray & bytes)
788 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
790 // decode host connection confirmation
791 if (bytes.size() != 14) {
793 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
794 throw MackieControlException (os.str());
797 // send version request
798 return MidiByteArray (2, 0x13, 0x00);
802 Surface::turn_it_on ()
810 _mcp.device_ready ();
812 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
816 update_view_mode_display (false);
818 // if (_mcp.device_info ().has_global_controls ()) {
819 // _mcp.update_global_button (Button::Read, _mcp.metering_active ());
824 Surface::write_sysex (const MidiByteArray & mba)
831 buf << sysex_hdr() << mba << MIDI::eox;
836 Surface::write_sysex (MIDI::byte msg)
839 buf << sysex_hdr() << msg << MIDI::eox;
844 Surface::n_strips (bool with_locked_strips) const
846 if (with_locked_strips) {
847 return strips.size();
852 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
853 if (!(*it)->locked()) {
861 Surface::nth_strip (uint32_t n) const
863 if (n > n_strips()) {
872 if (_mcp.device_info().has_timecode_display ()) {
873 display_timecode (string (10, '0'), string (10, ' '));
876 if (_mcp.device_info().has_two_character_display()) {
877 show_two_char_display (string (2, '0'), string (2, ' '));
880 if (_mcp.device_info().has_master_fader () && _master_fader) {
881 _port->write (_master_fader->zero ());
885 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
893 Surface::zero_controls ()
895 if (!_mcp.device_info().has_global_controls()) {
899 // turn off global buttons and leds
901 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
902 Control & control = **it;
903 if (!control.group().is_strip()) {
904 _port->write (control.zero());
908 // and the led ring for the master strip
911 _last_master_gain_written = 0.0f;
915 Surface::periodic (uint64_t now_usecs)
917 master_gain_changed();
918 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
919 (*s)->periodic (now_usecs);
924 Surface::redisplay (ARDOUR::microseconds_t now, bool force)
926 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
927 (*s)->redisplay (now, force);
932 Surface::write (const MidiByteArray& data)
937 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
942 Surface::update_strip_selection ()
944 Strips::iterator s = strips.begin();
945 for ( ; s != strips.end(); ++s) {
946 (*s)->update_selection_state();
951 Surface::map_stripables (const vector<boost::shared_ptr<Stripable> >& stripables)
953 vector<boost::shared_ptr<Stripable> >::const_iterator r;
954 Strips::iterator s = strips.begin();
956 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 stripables to %2 strips\n", stripables.size(), strips.size()));
958 for (r = stripables.begin(); r != stripables.end() && s != strips.end(); ++s) {
960 /* don't try to assign stripables to a locked strip. it won't
961 use it anyway, but if we do, then we get out of sync
962 with the proposed mapping.
965 if (!(*s)->locked()) {
966 (*s)->set_stripable (*r);
971 for (; s != strips.end(); ++s) {
972 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 being set to null stripable\n", (*s)->index()));
973 (*s)->set_stripable (boost::shared_ptr<Stripable>());
978 translate_seven_segment (char achar)
980 achar = toupper (achar);
982 if (achar >= 0x40 && achar <= 0x60) {
984 } else if (achar >= 0x21 && achar <= 0x3f) {
992 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
994 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
998 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
999 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
1001 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
1002 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
1004 _port->write (right);
1005 _port->write (left);
1009 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
1012 os << setfill('0') << setw(2) << value % 100;
1013 show_two_char_display (os.str());
1017 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
1019 if (!_active || !_mcp.device_info().has_timecode_display()) {
1022 // if there's no change, send nothing, not even sysex header
1023 if (timecode == last_timecode) return;
1025 // length sanity checking
1026 string local_timecode = timecode;
1028 // truncate to 10 characters
1029 if (local_timecode.length() > 10) {
1030 local_timecode = local_timecode.substr (0, 10);
1033 // pad to 10 characters
1034 while (local_timecode.length() < 10) {
1035 local_timecode += " ";
1038 // translate characters.
1039 // Only the characters that actually changed are sent.
1040 int position = 0x3f;
1042 for (i = local_timecode.length () - 1; i >= 0; i--) {
1044 if (local_timecode[i] == last_timecode[i]) {
1047 MidiByteArray retval (2, 0xb0, position);
1048 retval << translate_seven_segment (local_timecode[i]);
1049 _port->write (retval);
1054 Surface::update_flip_mode_display ()
1056 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1057 (*s)->flip_mode_changed ();
1062 Surface::subview_mode_changed ()
1064 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1065 (*s)->subview_mode_changed ();
1070 Surface::update_view_mode_display (bool with_helpful_text)
1079 switch (_mcp.view_mode()) {
1080 case MackieControlProtocol::Mixer:
1081 show_two_char_display ("Mx");
1083 text = _("Mixer View");
1085 case MackieControlProtocol::AudioTracks:
1086 show_two_char_display ("AT");
1087 id = Button::AudioTracks;
1088 text = _("Audio Tracks");
1090 case MackieControlProtocol::MidiTracks:
1091 show_two_char_display ("MT");
1092 id = Button::MidiTracks;
1093 text = _("MIDI Tracks");
1095 case MackieControlProtocol::Plugins:
1096 show_two_char_display ("PL");
1097 id = Button::Plugin;
1098 text = _("Plugins");
1100 case MackieControlProtocol::Busses:
1101 show_two_char_display ("BS");
1102 id = Button::Busses;
1103 if (Profile->get_mixbus()) {
1104 text = _("Mixbusses");
1109 case MackieControlProtocol::Auxes:
1110 show_two_char_display ("Au");
1114 case MackieControlProtocol::Hidden:
1115 show_two_char_display ("HI");
1116 id = Button::Outputs;
1117 text = _("Hidden Tracks");
1119 case MackieControlProtocol::Selected:
1120 show_two_char_display ("SE");
1122 text = _("Selected Tracks");
1128 vector<int> view_mode_buttons;
1129 view_mode_buttons.push_back (Button::View);
1130 view_mode_buttons.push_back (Button::Busses);
1131 view_mode_buttons.push_back (Button::Plugin);
1132 view_mode_buttons.push_back (Button::AudioTracks);
1133 view_mode_buttons.push_back (Button::MidiTracks);
1134 view_mode_buttons.push_back (Button::Aux);
1135 view_mode_buttons.push_back (Button::Outputs);
1136 view_mode_buttons.push_back (Button::User);
1140 for (vector<int>::iterator i = view_mode_buttons.begin(); i != view_mode_buttons.end(); ++i) {
1141 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1143 if (x != controls_by_device_independent_id.end()) {
1144 Button* button = dynamic_cast<Button*> (x->second);
1149 _port->write (button->set_state (onoff));
1155 if (with_helpful_text && !text.empty()) {
1156 display_message_for (text, 1000);
1161 Surface::say_hello ()
1163 /* wakeup for Mackie Control */
1164 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1165 _port->write (wakeup);
1166 wakeup[4] = 0x15; /* wakup Mackie XT */
1167 _port->write (wakeup);
1168 wakeup[4] = 0x10; /* wakeup Logic Control */
1169 _port->write (wakeup);
1170 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1171 _port->write (wakeup);
1175 Surface::next_jog_mode ()
1180 Surface::set_jog_mode (JogWheel::Mode)
1185 Surface::stripable_is_locked_to_strip (boost::shared_ptr<Stripable> stripable) const
1187 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1188 if ((*s)->stripable() == stripable && (*s)->locked()) {
1196 Surface::stripable_is_mapped (boost::shared_ptr<Stripable> stripable) const
1198 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1199 if ((*s)->stripable() == stripable) {
1208 Surface::notify_metering_state_changed()
1210 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1211 (*s)->notify_metering_state_changed ();
1219 /* reset msg for Mackie Control */
1230 Surface::toggle_backlight ()
1233 int onoff = random() %2;
1235 msg << sysex_hdr ();
1237 msg << (onoff ? 0x1 : 0x0);
1244 Surface::recalibrate_faders ()
1248 msg << sysex_hdr ();
1257 Surface::set_touch_sensitivity (int sensitivity)
1259 /* NOTE: assumed called from GUI code, hence sleep() */
1261 /* sensitivity already clamped by caller */
1266 msg << sysex_hdr ();
1268 msg << 0xff; /* overwritten for each fader below */
1269 msg << (sensitivity & 0x7f);
1272 for (int fader = 0; fader < 9; ++fader) {
1280 Surface::hui_heartbeat ()
1286 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1291 Surface::connected ()
1293 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1297 if (_mcp.device_info().no_handshake()) {
1303 Surface::display_line (string const& msg, int line_num)
1305 MidiByteArray midi_msg;
1306 midi_msg << sysex_hdr ();
1308 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1310 * correspond to line
1315 midi_msg.insert (midi_msg.end(), 55, ' ');
1319 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1320 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1321 string::size_type len = ascii.length();
1324 midi_msg << ascii.substr (0, 55);
1328 for (string::size_type i = len; i < 55; ++i) {
1334 midi_msg << MIDI::eox;
1339 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1341 * @param msg is assumed to be UTF-8 encoded, and will be converted
1342 * to ASCII with an underscore as fallback character before being
1343 * sent to the device.
1346 Surface::display_message_for (string const& msg, uint64_t msecs)
1348 string::size_type newline;
1350 if ((newline = msg.find ('\n')) == string::npos) {
1352 _port->write (display_line (msg, 0));
1353 _port->write (display_line (string(), 1));
1355 } else if (newline == 0) {
1357 _port->write (display_line (string(), 0));
1358 _port->write (display_line (msg.substr (1), 1));
1362 string first_line = msg.substr (0, newline-1);
1363 string second_line = msg.substr (newline+1);
1365 _port->write (display_line (first_line, 0));
1366 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1369 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1370 (*s)->block_screen_display_for (msecs);