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->add_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) {
265 XMLProperty const* prop = (*c)->property (X_("name"));
267 if (prop->value() == _name) {
278 XMLNode* portnode = mynode->child (X_("Port"));
280 if (_port->set_state (*portnode, version)) {
289 Surface::sysex_hdr() const
292 case mcu: return mackie_sysex_hdr;
293 case ext: return mackie_sysex_hdr_xt;
295 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
296 return mackie_sysex_hdr;
299 static GlobalControlDefinition mackie_global_controls[] = {
300 { "external", Pot::External, Pot::factory, "none" },
301 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
302 { "timecode", Led::Timecode, Led::factory, "none" },
303 { "beats", Led::Beats, Led::factory, "none" },
304 { "solo", Led::RudeSolo, Led::factory, "none" },
305 { "relay_click", Led::RelayClick, Led::factory, "none" },
306 { "", 0, Led::factory, "" }
310 Surface::init_controls()
314 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
315 groups["assignment"] = new Group ("assignment");
316 groups["automation"] = new Group ("automation");
317 groups["bank"] = new Group ("bank");
318 groups["cursor"] = new Group ("cursor");
319 groups["display"] = new Group ("display");
320 groups["function select"] = new Group ("function select");
321 groups["global view"] = new Group ("global view");
322 groups["master"] = new Group ("master");
323 groups["modifiers"] = new Group ("modifiers");
324 groups["none"] = new Group ("none");
325 groups["transport"] = new Group ("transport");
326 groups["user"] = new Group ("user");
327 groups["utilities"] = new Group ("utilities");
329 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
330 if (_mcp.device_info().has_jog_wheel()) {
331 _jog_wheel = new Mackie::JogWheel (_mcp);
334 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
335 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
336 group = groups[mackie_global_controls[n].group_name];
337 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
338 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
341 /* add global buttons */
342 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
343 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
345 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
346 group = groups[b->second.group];
347 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
352 Surface::init_strips (uint32_t n)
354 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
356 for (uint32_t i = 0; i < n; ++i) {
360 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
362 Strip* strip = new Strip (*this, name, i, strip_buttons);
364 groups[name] = strip;
365 strips.push_back (strip);
370 Surface::master_monitor_may_have_changed ()
372 if (_number == _mcp.device_info().master_position()) {
378 Surface::setup_master ()
380 boost::shared_ptr<Stripable> m;
382 if ((m = _mcp.get_session().monitor_out()) == 0) {
383 m = _mcp.get_session().master_out();
388 _master_fader->set_control (boost::shared_ptr<AutomationControl>());
390 master_connection.disconnect ();
394 if (!_master_fader) {
395 Groups::iterator group_it;
397 group_it = groups.find("master");
399 if (group_it == groups.end()) {
400 groups["master"] = master_group = new Group ("master");
402 master_group = group_it->second;
405 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *master_group));
407 DeviceInfo device_info = _mcp.device_info();
408 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
409 Button* bb = dynamic_cast<Button*> (Button::factory (
411 Button::MasterFaderTouch,
417 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
418 number(), Button::MasterFaderTouch, bb->id()));
420 master_connection.disconnect ();
423 _master_fader->set_control (m->gain_control());
424 m->gain_control()->Changed.connect (master_connection, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
425 _last_master_gain_written = FLT_MAX; /* some essentially impossible value */
426 master_gain_changed ();
430 Surface::master_gain_changed ()
432 if (!_master_fader) {
436 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
441 float normalized_position = ac->internal_to_interface (ac->get_value());
442 if (normalized_position == _last_master_gain_written) {
446 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
448 _port->write (_master_fader->set_position (normalized_position));
449 _last_master_gain_written = normalized_position;
453 Surface::scaled_delta (float delta, float current_speed)
455 /* XXX needs work before use */
456 const float sign = delta < 0.0 ? -1.0 : 1.0;
458 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
462 Surface::display_bank_start (uint32_t current_bank)
464 if (current_bank == 0) {
465 // send Ar. to 2-char display on the master
466 show_two_char_display ("Ar", "..");
468 // write the current first remote_id to the 2-char display
469 show_two_char_display (current_bank);
474 Surface::blank_jog_ring ()
476 Control* control = controls_by_device_independent_id[Jog::ID];
479 Pot* pot = dynamic_cast<Pot*> (control);
481 _port->write (pot->set (0.0, false, Pot::spread));
487 Surface::scrub_scaling_factor () const
493 Surface::connect_to_signals ()
498 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
499 number(), _port->input_port().name()));
501 MIDI::Parser* p = _port->input_port().parser();
504 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
505 /* V-Pot messages are Controller */
506 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
507 /* Button messages are NoteOn */
508 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
509 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
510 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
511 /* Fader messages are Pitchbend */
513 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
514 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
517 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()));
524 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
526 /* Pitchbend messages are fader position messages. Nothing in the data we get
527 * from the MIDI::Parser conveys the fader ID, which was given by the
528 * channel ID in the status byte.
530 * Instead, we have used bind() to supply the fader-within-strip ID
531 * when we connected to the per-channel pitchbend events.
534 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
535 fader_id, pb, _number, pb/16384.0));
537 if (_mcp.device_info().no_handshake()) {
541 Fader* fader = faders[fader_id];
544 Strip* strip = dynamic_cast<Strip*> (&fader->group());
545 float pos = pb / 16384.0;
547 strip->handle_fader (*fader, pos);
549 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
551 fader->set_value (pos); // alter master gain
552 _port->write (fader->set_position (pos)); // write back value (required for servo)
555 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
560 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
562 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
564 if (_mcp.device_info().no_handshake()) {
568 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
572 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
576 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
577 Fader* fader = faders[ev->note_number];
579 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
583 Strip* strip = dynamic_cast<Strip*> (&fader->group());
585 if (ev->velocity > 64) {
586 strip->handle_fader_touch (*fader, true);
588 strip->handle_fader_touch (*fader, false);
594 Button* button = buttons[ev->note_number];
598 if (ev->velocity > 64) {
602 Strip* strip = dynamic_cast<Strip*> (&button->group());
605 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
606 strip->index(), button->name(), (ev->velocity > 64)));
607 strip->handle_button (*button, ev->velocity > 64 ? press : release);
610 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
611 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
614 if (ev->velocity <= 64) {
619 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
622 /* button release should reset timer AFTER handler(s) have run */
626 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
628 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
630 if (_mcp.device_info().no_handshake()) {
634 Pot* pot = pots[ev->controller_number];
636 // bit 6 gives the sign
637 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
638 // bits 0..5 give the velocity. we interpret this as "ticks
639 // moved before this message was sent"
640 float ticks = (ev->value & 0x3f);
642 /* euphonix and perhaps other devices send zero
643 when they mean 1, we think.
649 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_SHIFT) {
650 delta = sign * (ticks / (float) 0xff);
652 delta = sign * (ticks / (float) 0x3f);
656 if (ev->controller_number == Jog::ID && _jog_wheel) {
658 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
659 _jog_wheel->jog_event (delta);
662 // add external (pedal?) control here
667 Strip* strip = dynamic_cast<Strip*> (&pot->group());
669 strip->handle_pot (*pot, delta);
674 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
676 MidiByteArray bytes (count, raw_bytes);
678 if (_mcp.device_info().no_handshake()) {
682 /* always save the device type ID so that our outgoing sysex messages
687 mackie_sysex_hdr[4] = bytes[4];
689 mackie_sysex_hdr_xt[4] = bytes[4];
695 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
698 LCP: Connection Challenge
700 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
701 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
702 write_sysex (host_connection_query (bytes));
705 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
713 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
715 /* Behringer X-Touch Compact: Device Ready
717 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Behringer X-Touch Compact ready, current status = %1\n", _active));
721 case 0x03: /* LCP Connection Confirmation */
722 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
723 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
724 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
725 write_sysex (host_connection_confirmation (bytes));
730 case 0x04: /* LCP: Confirmation Denied */
731 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
732 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
737 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
738 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("unknown device ID byte %1", (int) bytes[5]));
739 error << "MCP: unknown sysex: " << bytes << endmsg;
744 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
747 back_insert_iterator<MidiByteArray> back (l);
748 copy (begin, end, back);
750 MidiByteArray retval;
752 // this is how to calculate the response to the challenge.
753 // from the Logic docs.
754 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
755 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
756 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
757 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
763 Surface::host_connection_query (MidiByteArray & bytes)
765 MidiByteArray response;
767 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
768 /* not a Logic Control device - no response required */
772 // handle host connection query
773 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
775 if (bytes.size() != 18) {
776 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
780 // build and send host connection reply
782 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
783 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
788 Surface::host_connection_confirmation (const MidiByteArray & bytes)
790 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
792 // decode host connection confirmation
793 if (bytes.size() != 14) {
795 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
796 throw MackieControlException (os.str());
799 // send version request
800 return MidiByteArray (2, 0x13, 0x00);
804 Surface::turn_it_on ()
812 _mcp.device_ready ();
814 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
818 update_view_mode_display (false);
820 // if (_mcp.device_info ().has_global_controls ()) {
821 // _mcp.update_global_button (Button::Read, _mcp.metering_active ());
826 Surface::write_sysex (const MidiByteArray & mba)
833 buf << sysex_hdr() << mba << MIDI::eox;
838 Surface::write_sysex (MIDI::byte msg)
841 buf << sysex_hdr() << msg << MIDI::eox;
846 Surface::n_strips (bool with_locked_strips) const
848 if (with_locked_strips) {
849 return strips.size();
854 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
855 if (!(*it)->locked()) {
863 Surface::nth_strip (uint32_t n) const
865 if (n > n_strips()) {
874 if (_mcp.device_info().has_timecode_display ()) {
875 display_timecode (string (10, '0'), string (10, ' '));
878 if (_mcp.device_info().has_two_character_display()) {
879 show_two_char_display (string (2, '0'), string (2, ' '));
882 if (_mcp.device_info().has_master_fader () && _master_fader) {
883 _port->write (_master_fader->zero ());
887 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
895 Surface::zero_controls ()
897 if (!_mcp.device_info().has_global_controls()) {
901 // turn off global buttons and leds
903 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
904 Control & control = **it;
905 if (!control.group().is_strip()) {
906 _port->write (control.zero());
910 // and the led ring for the master strip
913 _last_master_gain_written = 0.0f;
917 Surface::periodic (uint64_t now_usecs)
919 master_gain_changed();
920 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
921 (*s)->periodic (now_usecs);
926 Surface::redisplay (ARDOUR::microseconds_t now, bool force)
928 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
929 (*s)->redisplay (now, force);
934 Surface::write (const MidiByteArray& data)
939 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
944 Surface::map_stripables (const vector<boost::shared_ptr<Stripable> >& stripables)
946 vector<boost::shared_ptr<Stripable> >::const_iterator r;
947 Strips::iterator s = strips.begin();
949 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 stripables to %2 strips\n", stripables.size(), strips.size()));
951 for (r = stripables.begin(); r != stripables.end() && s != strips.end(); ++s) {
953 /* don't try to assign stripables to a locked strip. it won't
954 use it anyway, but if we do, then we get out of sync
955 with the proposed mapping.
958 if (!(*s)->locked()) {
959 (*s)->set_stripable (*r);
964 for (; s != strips.end(); ++s) {
965 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 being set to null stripable\n", (*s)->index()));
966 (*s)->set_stripable (boost::shared_ptr<Stripable>());
971 translate_seven_segment (char achar)
973 achar = toupper (achar);
975 if (achar >= 0x40 && achar <= 0x60) {
977 } else if (achar >= 0x21 && achar <= 0x3f) {
985 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
987 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
991 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
992 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
994 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
995 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
997 _port->write (right);
1002 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
1005 os << setfill('0') << setw(2) << value % 100;
1006 show_two_char_display (os.str());
1010 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
1012 if (!_active || !_mcp.device_info().has_timecode_display()) {
1015 // if there's no change, send nothing, not even sysex header
1016 if (timecode == last_timecode) return;
1018 // length sanity checking
1019 string local_timecode = timecode;
1021 // truncate to 10 characters
1022 if (local_timecode.length() > 10) {
1023 local_timecode = local_timecode.substr (0, 10);
1026 // pad to 10 characters
1027 while (local_timecode.length() < 10) {
1028 local_timecode += " ";
1031 // translate characters.
1032 // Only the characters that actually changed are sent.
1033 int position = 0x3f;
1035 for (i = local_timecode.length () - 1; i >= 0; i--) {
1037 if (local_timecode[i] == last_timecode[i]) {
1040 MidiByteArray retval (2, 0xb0, position);
1041 retval << translate_seven_segment (local_timecode[i]);
1042 _port->write (retval);
1047 Surface::update_flip_mode_display ()
1049 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1050 (*s)->flip_mode_changed ();
1055 Surface::subview_mode_changed ()
1057 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1058 (*s)->subview_mode_changed ();
1063 Surface::update_view_mode_display (bool with_helpful_text)
1072 switch (_mcp.view_mode()) {
1073 case MackieControlProtocol::Mixer:
1074 show_two_char_display ("Mx");
1076 text = _("Mixer View");
1078 case MackieControlProtocol::AudioTracks:
1079 show_two_char_display ("AT");
1080 id = Button::AudioTracks;
1081 text = _("Audio Tracks");
1083 case MackieControlProtocol::MidiTracks:
1084 show_two_char_display ("MT");
1085 id = Button::MidiTracks;
1086 text = _("MIDI Tracks");
1088 case MackieControlProtocol::Plugins:
1089 show_two_char_display ("PL");
1090 id = Button::Plugin;
1091 text = _("Plugins");
1093 case MackieControlProtocol::Busses:
1094 show_two_char_display ("BS");
1095 id = Button::Busses;
1096 if (Profile->get_mixbus()) {
1097 text = _("Mixbusses");
1102 case MackieControlProtocol::Auxes:
1103 show_two_char_display ("Au");
1107 case MackieControlProtocol::Hidden:
1108 show_two_char_display ("HI");
1109 id = Button::Outputs;
1110 text = _("Hidden Tracks");
1112 case MackieControlProtocol::Selected:
1113 show_two_char_display ("SE");
1115 text = _("Selected Tracks");
1121 vector<int> view_mode_buttons;
1122 view_mode_buttons.push_back (Button::View);
1123 view_mode_buttons.push_back (Button::Busses);
1124 view_mode_buttons.push_back (Button::Plugin);
1125 view_mode_buttons.push_back (Button::AudioTracks);
1126 view_mode_buttons.push_back (Button::MidiTracks);
1127 view_mode_buttons.push_back (Button::Aux);
1128 view_mode_buttons.push_back (Button::Outputs);
1129 view_mode_buttons.push_back (Button::User);
1133 for (vector<int>::iterator i = view_mode_buttons.begin(); i != view_mode_buttons.end(); ++i) {
1134 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1136 if (x != controls_by_device_independent_id.end()) {
1137 Button* button = dynamic_cast<Button*> (x->second);
1142 _port->write (button->set_state (onoff));
1148 if (with_helpful_text && !text.empty()) {
1149 display_message_for (text, 1000);
1154 Surface::say_hello ()
1156 /* wakeup for Mackie Control */
1157 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1158 _port->write (wakeup);
1159 wakeup[4] = 0x15; /* wakup Mackie XT */
1160 _port->write (wakeup);
1161 wakeup[4] = 0x10; /* wakeup Logic Control */
1162 _port->write (wakeup);
1163 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1164 _port->write (wakeup);
1168 Surface::next_jog_mode ()
1173 Surface::set_jog_mode (JogWheel::Mode)
1178 Surface::stripable_is_locked_to_strip (boost::shared_ptr<Stripable> stripable) const
1180 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1181 if ((*s)->stripable() == stripable && (*s)->locked()) {
1189 Surface::stripable_is_mapped (boost::shared_ptr<Stripable> stripable) const
1191 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1192 if ((*s)->stripable() == stripable) {
1201 Surface::notify_metering_state_changed()
1203 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1204 (*s)->notify_metering_state_changed ();
1212 /* reset msg for Mackie Control */
1223 Surface::toggle_backlight ()
1226 int onoff = random() %2;
1228 msg << sysex_hdr ();
1230 msg << (onoff ? 0x1 : 0x0);
1237 Surface::recalibrate_faders ()
1241 msg << sysex_hdr ();
1250 Surface::set_touch_sensitivity (int sensitivity)
1252 /* NOTE: assumed called from GUI code, hence sleep() */
1254 /* sensitivity already clamped by caller */
1259 msg << sysex_hdr ();
1261 msg << 0xff; /* overwritten for each fader below */
1262 msg << (sensitivity & 0x7f);
1265 for (int fader = 0; fader < 9; ++fader) {
1273 Surface::hui_heartbeat ()
1279 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1284 Surface::connected ()
1286 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1290 if (_mcp.device_info().no_handshake()) {
1296 Surface::display_line (string const& msg, int line_num)
1298 MidiByteArray midi_msg;
1299 midi_msg << sysex_hdr ();
1301 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1303 * correspond to line
1308 midi_msg.insert (midi_msg.end(), 55, ' ');
1312 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1313 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1314 string::size_type len = ascii.length();
1317 midi_msg << ascii.substr (0, 55);
1321 for (string::size_type i = len; i < 55; ++i) {
1327 midi_msg << MIDI::eox;
1332 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1334 * @param msg is assumed to be UTF-8 encoded, and will be converted
1335 * to ASCII with an underscore as fallback character before being
1336 * sent to the device.
1339 Surface::display_message_for (string const& msg, uint64_t msecs)
1341 string::size_type newline;
1343 if ((newline = msg.find ('\n')) == string::npos) {
1345 _port->write (display_line (msg, 0));
1346 _port->write (display_line (string(), 1));
1348 } else if (newline == 0) {
1350 _port->write (display_line (string(), 0));
1351 _port->write (display_line (msg.substr (1), 1));
1355 string first_line = msg.substr (0, newline-1);
1356 string second_line = msg.substr (newline+1);
1358 _port->write (display_line (first_line, 0));
1359 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1362 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1363 (*s)->block_screen_display_for (msecs);