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);
88 // The MCU sysex header for QCon Control surface
89 static MidiByteArray mackie_sysex_hdr_qcon (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
91 // The MCU sysex header for QCon Control - extender
92 // The extender differs from Mackie by 4th bit - it's same like for main control surface (for display)
93 static MidiByteArray mackie_sysex_hdr_xt_qcon (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
96 static MidiByteArray empty_midi_byte_array;
98 Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype)
102 , _name (device_name)
107 , _last_master_gain_written (-0.0f)
108 , connection_state (0)
111 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
114 _port = new SurfacePort (*this);
116 throw failed_constructor ();
119 /* only the first Surface object has global controls */
120 /* lets use master_position instead */
121 uint32_t mp = _mcp.device_info().master_position();
123 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
124 if (_mcp.device_info().has_global_controls()) {
126 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
129 if (_mcp.device_info().has_master_fader()) {
131 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
135 uint32_t n = _mcp.device_info().strip_cnt();
139 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
142 if (_mcp.device_info().uses_ipmidi()) {
143 /* ipMIDI port already exists, we can just assume that we're
146 * If the user still hasn't connected the ipMIDI surface and/or
147 * turned it on, then they have to press "Discover Mackie
148 * Devices" in the GUI at the right time.
151 connection_state |= (InputConnected|OutputConnected);
155 connect_to_signals ();
157 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
162 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
165 g_source_destroy (input_source);
169 // delete groups (strips)
170 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
174 // delete controls (global buttons, master fader etc)
175 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
181 // the ports take time to release and we may be rebuilding right away
182 // in the case of changing devices.
184 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
188 Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
194 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
195 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
197 if (ni == name1 || ni == name2) {
199 connection_state |= InputConnected;
201 connection_state &= ~InputConnected;
203 } else if (no == name1 || no == name2) {
205 connection_state |= OutputConnected;
207 connection_state &= ~OutputConnected;
214 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
216 /* this will send a device query message, which should
217 result in a response that will kick off device type
218 discovery and activation of the surface(s).
220 The intended order of events is:
222 - each surface sends a device query message
223 - devices respond with either MCP or LCP response (sysex in both
225 - sysex message causes Surface::turn_it_on() which tells the
226 MCP object that the surface is ready, and sets up strip
227 displays and binds faders and buttons for that surface
229 In the case of LCP, where this is a handshake process that could
230 fail, the response process to the initial sysex after a device query
231 will mark the surface inactive, which won't shut anything down
232 but will stop any writes to the device.
234 Note: there are no known cases of the handshake process failing.
236 We actually can't initiate this in this callback, so we have
237 to queue it with the MCP event loop.
240 /* XXX this is a horrible hack. Without a short sleep here,
241 something prevents the device wakeup messages from being
242 sent and/or the responses from being received.
249 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
253 return true; /* connection status changed */
259 XMLNode* node = new XMLNode (X_("Surface"));
260 node->set_property (X_("name"), _name);
261 node->add_child_nocopy (_port->get_state());
266 Surface::set_state (const XMLNode& node, int version)
268 /* Look for a node named after the device we're part of */
270 XMLNodeList const& children = node.children();
273 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
275 if ((*c)->get_property (X_("name"), name) && name == _name) {
285 XMLNode* portnode = mynode->child (X_("Port"));
287 if (_port->set_state (*portnode, version)) {
296 Surface::sysex_hdr() const
300 if (_mcp.device_info().is_qcon()) {
301 return mackie_sysex_hdr_qcon;
303 return mackie_sysex_hdr;
306 if(_mcp.device_info().is_qcon()) {
307 return mackie_sysex_hdr_xt_qcon;
309 return mackie_sysex_hdr_xt;
312 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
313 return mackie_sysex_hdr;
316 static GlobalControlDefinition mackie_global_controls[] = {
317 { "external", Pot::External, Pot::factory, "none" },
318 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
319 { "timecode", Led::Timecode, Led::factory, "none" },
320 { "beats", Led::Beats, Led::factory, "none" },
321 { "solo", Led::RudeSolo, Led::factory, "none" },
322 { "relay_click", Led::RelayClick, Led::factory, "none" },
323 { "", 0, Led::factory, "" }
327 Surface::init_controls()
331 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
332 groups["assignment"] = new Group ("assignment");
333 groups["automation"] = new Group ("automation");
334 groups["bank"] = new Group ("bank");
335 groups["cursor"] = new Group ("cursor");
336 groups["display"] = new Group ("display");
337 groups["function select"] = new Group ("function select");
338 groups["global view"] = new Group ("global view");
339 groups["master"] = new Group ("master");
340 groups["modifiers"] = new Group ("modifiers");
341 groups["none"] = new Group ("none");
342 groups["transport"] = new Group ("transport");
343 groups["user"] = new Group ("user");
344 groups["utilities"] = new Group ("utilities");
346 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
347 if (_mcp.device_info().has_jog_wheel()) {
348 _jog_wheel = new Mackie::JogWheel (_mcp);
351 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
352 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
353 group = groups[mackie_global_controls[n].group_name];
354 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
355 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
358 /* add global buttons */
359 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
360 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
362 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
363 group = groups[b->second.group];
364 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
369 Surface::init_strips (uint32_t n)
371 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
373 for (uint32_t i = 0; i < n; ++i) {
377 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
379 Strip* strip = new Strip (*this, name, i, strip_buttons);
381 groups[name] = strip;
382 strips.push_back (strip);
387 Surface::master_monitor_may_have_changed ()
389 if (_number == _mcp.device_info().master_position()) {
395 Surface::setup_master ()
397 boost::shared_ptr<Stripable> m;
399 if ((m = _mcp.get_session().monitor_out()) == 0) {
400 m = _mcp.get_session().master_out();
405 _master_fader->set_control (boost::shared_ptr<AutomationControl>());
407 master_connection.disconnect ();
411 if (!_master_fader) {
412 Groups::iterator group_it;
414 group_it = groups.find("master");
416 if (group_it == groups.end()) {
417 groups["master"] = master_group = new Group ("master");
419 master_group = group_it->second;
422 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *master_group));
424 DeviceInfo device_info = _mcp.device_info();
425 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
426 Button* bb = dynamic_cast<Button*> (Button::factory (
428 Button::MasterFaderTouch,
434 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
435 number(), Button::MasterFaderTouch, bb->id()));
437 master_connection.disconnect ();
440 _master_fader->set_control (m->gain_control());
441 m->gain_control()->Changed.connect (master_connection, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
442 _last_master_gain_written = FLT_MAX; /* some essentially impossible value */
443 master_gain_changed ();
447 Surface::master_gain_changed ()
449 if (!_master_fader) {
453 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
458 float normalized_position = ac->internal_to_interface (ac->get_value());
459 if (normalized_position == _last_master_gain_written) {
463 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
465 _port->write (_master_fader->set_position (normalized_position));
466 _last_master_gain_written = normalized_position;
470 Surface::scaled_delta (float delta, float current_speed)
472 /* XXX needs work before use */
473 const float sign = delta < 0.0 ? -1.0 : 1.0;
475 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
479 Surface::display_bank_start (uint32_t current_bank)
481 if (current_bank == 0) {
482 // send Ar. to 2-char display on the master
483 show_two_char_display ("Ar", "..");
485 // write the current first remote_id to the 2-char display
486 show_two_char_display (current_bank);
491 Surface::blank_jog_ring ()
493 Control* control = controls_by_device_independent_id[Jog::ID];
496 Pot* pot = dynamic_cast<Pot*> (control);
498 _port->write (pot->set (0.0, false, Pot::spread));
504 Surface::scrub_scaling_factor () const
510 Surface::connect_to_signals ()
515 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
516 number(), _port->input_port().name()));
518 MIDI::Parser* p = _port->input_port().parser();
521 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
522 /* V-Pot messages are Controller */
523 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
524 /* Button messages are NoteOn */
525 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
526 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
527 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
528 /* Fader messages are Pitchbend */
530 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
531 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
534 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()));
541 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
543 /* Pitchbend messages are fader position messages. Nothing in the data we get
544 * from the MIDI::Parser conveys the fader ID, which was given by the
545 * channel ID in the status byte.
547 * Instead, we have used bind() to supply the fader-within-strip ID
548 * when we connected to the per-channel pitchbend events.
551 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
552 fader_id, pb, _number, pb/16384.0));
554 if (_mcp.device_info().no_handshake()) {
558 Fader* fader = faders[fader_id];
561 Strip* strip = dynamic_cast<Strip*> (&fader->group());
562 float pos = pb / 16384.0;
564 strip->handle_fader (*fader, pos);
566 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
568 fader->set_value (pos); // alter master gain
569 _port->write (fader->set_position (pos)); // write back value (required for servo)
572 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
577 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
579 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
581 if (_mcp.device_info().no_handshake()) {
585 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
589 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
593 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
594 Fader* fader = faders[ev->note_number];
596 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
600 Strip* strip = dynamic_cast<Strip*> (&fader->group());
602 if (ev->velocity > 64) {
603 strip->handle_fader_touch (*fader, true);
605 strip->handle_fader_touch (*fader, false);
611 Button* button = buttons[ev->note_number];
615 if (ev->velocity > 64) {
619 Strip* strip = dynamic_cast<Strip*> (&button->group());
622 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
623 strip->index(), button->name(), (ev->velocity > 64)));
624 strip->handle_button (*button, ev->velocity > 64 ? press : release);
627 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
628 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
631 if (ev->velocity <= 64) {
636 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
639 /* button release should reset timer AFTER handler(s) have run */
643 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
645 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
647 if (_mcp.device_info().no_handshake()) {
651 Pot* pot = pots[ev->controller_number];
653 // bit 6 gives the sign
654 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
655 // bits 0..5 give the velocity. we interpret this as "ticks
656 // moved before this message was sent"
657 float ticks = (ev->value & 0x3f);
659 /* euphonix and perhaps other devices send zero
660 when they mean 1, we think.
666 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_SHIFT) {
667 delta = sign * (ticks / (float) 0xff);
669 delta = sign * (ticks / (float) 0x3f);
673 if (ev->controller_number == Jog::ID && _jog_wheel) {
675 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
676 _jog_wheel->jog_event (delta);
679 // add external (pedal?) control here
684 Strip* strip = dynamic_cast<Strip*> (&pot->group());
686 strip->handle_pot (*pot, delta);
691 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
693 MidiByteArray bytes (count, raw_bytes);
695 if (_mcp.device_info().no_handshake()) {
699 /* always save the device type ID so that our outgoing sysex messages
704 if (_mcp.device_info().is_qcon()) {
705 mackie_sysex_hdr_qcon[4] = bytes[4];
707 mackie_sysex_hdr[4] = bytes[4];
711 if (_mcp.device_info().is_qcon()) {
712 mackie_sysex_hdr_xt_qcon[4] = bytes[4];
714 mackie_sysex_hdr_xt[4] = bytes[4];
721 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
724 LCP: Connection Challenge
726 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
727 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
728 write_sysex (host_connection_query (bytes));
731 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
739 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
741 /* Behringer X-Touch Compact: Device Ready
743 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Behringer X-Touch Compact ready, current status = %1\n", _active));
747 case 0x03: /* LCP Connection Confirmation */
748 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
749 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
750 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
751 write_sysex (host_connection_confirmation (bytes));
756 case 0x04: /* LCP: Confirmation Denied */
757 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
758 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
763 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
764 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("unknown device ID byte %1", (int) bytes[5]));
765 error << "MCP: unknown sysex: " << bytes << endmsg;
770 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
773 back_insert_iterator<MidiByteArray> back (l);
774 copy (begin, end, back);
776 MidiByteArray retval;
778 // this is how to calculate the response to the challenge.
779 // from the Logic docs.
780 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
781 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
782 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
783 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
789 Surface::host_connection_query (MidiByteArray & bytes)
791 MidiByteArray response;
793 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
794 /* not a Logic Control device - no response required */
798 // handle host connection query
799 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
801 if (bytes.size() != 18) {
802 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
806 // build and send host connection reply
808 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
809 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
814 Surface::host_connection_confirmation (const MidiByteArray & bytes)
816 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
818 // decode host connection confirmation
819 if (bytes.size() != 14) {
821 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
822 throw MackieControlException (os.str());
825 // send version request
826 return MidiByteArray (2, 0x13, 0x00);
830 Surface::turn_it_on ()
838 _mcp.device_ready ();
840 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
844 update_view_mode_display (false);
846 // if (_mcp.device_info ().has_global_controls ()) {
847 // _mcp.update_global_button (Button::Read, _mcp.metering_active ());
852 Surface::write_sysex (const MidiByteArray & mba)
859 buf << sysex_hdr() << mba << MIDI::eox;
864 Surface::write_sysex (MIDI::byte msg)
867 buf << sysex_hdr() << msg << MIDI::eox;
872 Surface::n_strips (bool with_locked_strips) const
874 if (with_locked_strips) {
875 return strips.size();
880 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
881 if (!(*it)->locked()) {
889 Surface::nth_strip (uint32_t n) const
891 if (n > n_strips()) {
900 if (_mcp.device_info().has_timecode_display ()) {
901 display_timecode (string (10, '0'), string (10, ' '));
904 if (_mcp.device_info().has_two_character_display()) {
905 show_two_char_display (string (2, '0'), string (2, ' '));
908 if (_mcp.device_info().has_master_fader () && _master_fader) {
909 _port->write (_master_fader->zero ());
913 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
921 Surface::zero_controls ()
923 if (!_mcp.device_info().has_global_controls()) {
927 // turn off global buttons and leds
929 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
930 Control & control = **it;
931 if (!control.group().is_strip()) {
932 _port->write (control.zero());
936 // and the led ring for the master strip
939 _last_master_gain_written = 0.0f;
943 Surface::periodic (uint64_t now_usecs)
945 master_gain_changed();
946 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
947 (*s)->periodic (now_usecs);
952 Surface::redisplay (ARDOUR::microseconds_t now, bool force)
954 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
955 (*s)->redisplay (now, force);
960 Surface::write (const MidiByteArray& data)
965 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
970 Surface::update_strip_selection ()
972 Strips::iterator s = strips.begin();
973 for ( ; s != strips.end(); ++s) {
974 (*s)->update_selection_state();
979 Surface::map_stripables (const vector<boost::shared_ptr<Stripable> >& stripables)
981 vector<boost::shared_ptr<Stripable> >::const_iterator r;
982 Strips::iterator s = strips.begin();
984 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 stripables to %2 strips\n", stripables.size(), strips.size()));
986 for (r = stripables.begin(); r != stripables.end() && s != strips.end(); ++s) {
988 /* don't try to assign stripables to a locked strip. it won't
989 use it anyway, but if we do, then we get out of sync
990 with the proposed mapping.
993 if (!(*s)->locked()) {
994 (*s)->set_stripable (*r);
999 for (; s != strips.end(); ++s) {
1000 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 being set to null stripable\n", (*s)->index()));
1001 (*s)->set_stripable (boost::shared_ptr<Stripable>());
1006 translate_seven_segment (char achar)
1008 achar = toupper (achar);
1010 if (achar >= 0x40 && achar <= 0x60) {
1011 return achar - 0x40;
1012 } else if (achar >= 0x21 && achar <= 0x3f) {
1020 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
1022 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
1026 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
1027 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
1029 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
1030 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
1032 _port->write (right);
1033 _port->write (left);
1037 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
1040 os << setfill('0') << setw(2) << value % 100;
1041 show_two_char_display (os.str());
1045 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
1047 if (!_active || !_mcp.device_info().has_timecode_display()) {
1050 // if there's no change, send nothing, not even sysex header
1051 if (timecode == last_timecode) return;
1053 // length sanity checking
1054 string local_timecode = timecode;
1056 // truncate to 10 characters
1057 if (local_timecode.length() > 10) {
1058 local_timecode = local_timecode.substr (0, 10);
1061 // pad to 10 characters
1062 while (local_timecode.length() < 10) {
1063 local_timecode += " ";
1066 // translate characters.
1067 // Only the characters that actually changed are sent.
1068 int position = 0x3f;
1070 for (i = local_timecode.length () - 1; i >= 0; i--) {
1072 if (local_timecode[i] == last_timecode[i]) {
1075 MidiByteArray retval (2, 0xb0, position);
1076 retval << translate_seven_segment (local_timecode[i]);
1077 _port->write (retval);
1082 Surface::update_flip_mode_display ()
1084 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1085 (*s)->flip_mode_changed ();
1090 Surface::subview_mode_changed ()
1092 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1093 (*s)->subview_mode_changed ();
1098 Surface::update_view_mode_display (bool with_helpful_text)
1107 switch (_mcp.view_mode()) {
1108 case MackieControlProtocol::Mixer:
1109 show_two_char_display ("Mx");
1111 text = _("Mixer View");
1113 case MackieControlProtocol::AudioTracks:
1114 show_two_char_display ("AT");
1115 id = Button::AudioTracks;
1116 text = _("Audio Tracks");
1118 case MackieControlProtocol::MidiTracks:
1119 show_two_char_display ("MT");
1120 id = Button::MidiTracks;
1121 text = _("MIDI Tracks");
1123 case MackieControlProtocol::Plugins:
1124 show_two_char_display ("PL");
1125 id = Button::Plugin;
1126 text = _("Plugins");
1128 case MackieControlProtocol::Busses:
1129 show_two_char_display ("BS");
1130 id = Button::Busses;
1131 if (Profile->get_mixbus()) {
1132 text = _("Mixbusses");
1137 case MackieControlProtocol::Auxes:
1138 show_two_char_display ("Au");
1142 case MackieControlProtocol::Hidden:
1143 show_two_char_display ("HI");
1144 id = Button::Outputs;
1145 text = _("Hidden Tracks");
1147 case MackieControlProtocol::Selected:
1148 show_two_char_display ("SE");
1150 text = _("Selected Tracks");
1156 vector<int> view_mode_buttons;
1157 view_mode_buttons.push_back (Button::View);
1158 view_mode_buttons.push_back (Button::Busses);
1159 view_mode_buttons.push_back (Button::Plugin);
1160 view_mode_buttons.push_back (Button::AudioTracks);
1161 view_mode_buttons.push_back (Button::MidiTracks);
1162 view_mode_buttons.push_back (Button::Aux);
1163 view_mode_buttons.push_back (Button::Outputs);
1164 view_mode_buttons.push_back (Button::User);
1168 for (vector<int>::iterator i = view_mode_buttons.begin(); i != view_mode_buttons.end(); ++i) {
1169 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1171 if (x != controls_by_device_independent_id.end()) {
1172 Button* button = dynamic_cast<Button*> (x->second);
1177 _port->write (button->set_state (onoff));
1183 if (with_helpful_text && !text.empty()) {
1184 display_message_for (text, 1000);
1189 Surface::say_hello ()
1191 /* wakeup for Mackie Control */
1192 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1193 _port->write (wakeup);
1194 wakeup[4] = 0x15; /* wakup Mackie XT */
1195 _port->write (wakeup);
1196 wakeup[4] = 0x10; /* wakeup Logic Control */
1197 _port->write (wakeup);
1198 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1199 _port->write (wakeup);
1203 Surface::next_jog_mode ()
1208 Surface::set_jog_mode (JogWheel::Mode)
1213 Surface::stripable_is_locked_to_strip (boost::shared_ptr<Stripable> stripable) const
1215 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1216 if ((*s)->stripable() == stripable && (*s)->locked()) {
1224 Surface::stripable_is_mapped (boost::shared_ptr<Stripable> stripable) const
1226 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1227 if ((*s)->stripable() == stripable) {
1236 Surface::notify_metering_state_changed()
1238 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1239 (*s)->notify_metering_state_changed ();
1247 /* reset msg for Mackie Control */
1258 Surface::toggle_backlight ()
1261 int onoff = random() %2;
1263 msg << sysex_hdr ();
1265 msg << (onoff ? 0x1 : 0x0);
1272 Surface::recalibrate_faders ()
1276 msg << sysex_hdr ();
1285 Surface::set_touch_sensitivity (int sensitivity)
1287 /* NOTE: assumed called from GUI code, hence sleep() */
1289 /* sensitivity already clamped by caller */
1294 msg << sysex_hdr ();
1296 msg << 0xff; /* overwritten for each fader below */
1297 msg << (sensitivity & 0x7f);
1300 for (int fader = 0; fader < 9; ++fader) {
1308 Surface::hui_heartbeat ()
1314 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1319 Surface::connected ()
1321 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1325 if (_mcp.device_info().no_handshake()) {
1331 Surface::display_line (string const& msg, int line_num)
1333 MidiByteArray midi_msg;
1334 midi_msg << sysex_hdr ();
1336 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1338 * correspond to line
1343 midi_msg.insert (midi_msg.end(), 55, ' ');
1347 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1348 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1349 string::size_type len = ascii.length();
1352 midi_msg << ascii.substr (0, 55);
1356 for (string::size_type i = len; i < 55; ++i) {
1362 midi_msg << MIDI::eox;
1367 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1369 * @param msg is assumed to be UTF-8 encoded, and will be converted
1370 * to ASCII with an underscore as fallback character before being
1371 * sent to the device.
1374 Surface::display_message_for (string const& msg, uint64_t msecs)
1376 string::size_type newline;
1378 if ((newline = msg.find ('\n')) == string::npos) {
1380 _port->write (display_line (msg, 0));
1381 _port->write (display_line (string(), 1));
1383 } else if (newline == 0) {
1385 _port->write (display_line (string(), 0));
1386 _port->write (display_line (msg.substr (1), 1));
1390 string first_line = msg.substr (0, newline-1);
1391 string second_line = msg.substr (newline+1);
1393 _port->write (display_line (first_line, 0));
1394 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1397 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1398 (*s)->block_screen_display_for (msecs);