2 Copyright (C) 2006,2007 John Anderson
3 Copyright (C) 2012 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <glibmm/convert.h>
30 #include "midi++/port.h"
32 #include "pbd/compose.h"
33 #include "pbd/convert.h"
35 #include "ardour/amp.h"
36 #include "ardour/bundle.h"
37 #include "ardour/debug.h"
38 #include "ardour/midi_ui.h"
39 #include "ardour/meter.h"
40 #include "ardour/plugin_insert.h"
41 #include "ardour/pannable.h"
42 #include "ardour/panner.h"
43 #include "ardour/panner_shell.h"
44 #include "ardour/rc_configuration.h"
45 #include "ardour/route.h"
46 #include "ardour/session.h"
47 #include "ardour/send.h"
48 #include "ardour/track.h"
49 #include "ardour/midi_track.h"
50 #include "ardour/user_bundle.h"
51 #include "ardour/profile.h"
53 #include "mackie_control_protocol.h"
54 #include "surface_port.h"
65 using namespace ARDOUR;
67 using namespace ArdourSurface;
68 using namespace Mackie;
70 #ifndef timeradd /// only avail with __USE_BSD
71 #define timeradd(a,b,result) \
73 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
74 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
75 if ((result)->tv_usec >= 1000000) \
78 (result)->tv_usec -= 1000000; \
83 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
85 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
98 , _controls_locked (false)
99 , _transport_is_rolling (false)
100 , _metering_active (true)
101 , _block_screen_redisplay_until (0)
102 , return_to_vpot_mode_display_at (UINT64_MAX)
104 , _pan_mode (PanAzimuthAutomation)
105 , _last_gain_position_written (-1.0)
106 , _last_pan_azi_position_written (-1.0)
107 , _last_pan_width_position_written (-1.0)
108 , _last_trim_position_written (-1.0)
110 _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
111 _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
113 if (s.mcp().device_info().has_meters()) {
114 _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
117 for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
118 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
119 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
120 _surface->number(), index, Button::id_to_name (bb->bid()),
121 bb->id(), b->second.base_id));
127 /* surface is responsible for deleting all controls */
131 Strip::add (Control & control)
135 Group::add (control);
137 /* fader, vpot, meter were all set explicitly */
139 if ((button = dynamic_cast<Button*>(&control)) != 0) {
140 switch (button->bid()) {
141 case Button::RecEnable:
153 case Button::VSelect:
156 case Button::FaderTouch:
157 _fader_touch = button;
166 Strip::set_route (boost::shared_ptr<Route> r, bool /*with_messages*/)
168 if (_controls_locked) {
172 mb_pan_controllable.reset();
174 route_connections.drop_connections ();
176 _solo->set_control (boost::shared_ptr<AutomationControl>());
177 _mute->set_control (boost::shared_ptr<AutomationControl>());
178 _select->set_control (boost::shared_ptr<AutomationControl>());
179 _recenable->set_control (boost::shared_ptr<AutomationControl>());
180 _fader->set_control (boost::shared_ptr<AutomationControl>());
181 _vpot->set_control (boost::shared_ptr<AutomationControl>());
185 reset_saved_values ();
192 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping route %3\n",
193 _surface->number(), _index, _route->name()));
195 _solo->set_control (_route->solo_control());
196 _mute->set_control (_route->mute_control());
198 _route->solo_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
199 _route->listen_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
201 _route->mute_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
203 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control();
205 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
208 pan_control = _route->pan_width_control();
210 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
213 _route->gain_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
214 _route->PropertyChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
216 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
219 _recenable->set_control (trk->rec_enable_control());
220 trk->rec_enable_control()->Changed .connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
223 // TODO this works when a currently-banked route is made inactive, but not
224 // when a route is activated which should be currently banked.
226 _route->active_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_active_changed, this), ui_context());
227 _route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_route_deleted, this), ui_context());
229 /* setup legal VPot modes for this route */
231 possible_pot_parameters.clear();
233 if (_route->pan_azimuth_control()) {
234 possible_pot_parameters.push_back (PanAzimuthAutomation);
236 if (_route->pan_width_control()) {
237 possible_pot_parameters.push_back (PanWidthAutomation);
239 if (_route->pan_elevation_control()) {
240 possible_pot_parameters.push_back (PanElevationAutomation);
242 if (_route->pan_frontback_control()) {
243 possible_pot_parameters.push_back (PanFrontBackAutomation);
245 if (_route->pan_lfe_control()) {
246 possible_pot_parameters.push_back (PanLFEAutomation);
249 _pan_mode = PanAzimuthAutomation;
251 if (_surface->mcp().subview_mode() == MackieControlProtocol::None) {
252 set_vpot_parameter (_pan_mode);
255 _fader->set_control (_route->gain_control());
267 // The active V-pot control may not be active for this strip
268 // But if we zero it in the controls function it may erase
269 // the one we do want
270 _surface->write (_vpot->zero());
272 notify_solo_changed ();
273 notify_mute_changed ();
274 notify_gain_changed ();
275 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
276 notify_panner_azi_changed ();
277 notify_panner_width_changed ();
278 notify_record_enable_changed ();
279 notify_processor_changed ();
283 Strip::notify_solo_changed ()
285 if (_route && _solo) {
286 _surface->write (_solo->set_state ((_route->soloed() || _route->listening_via_monitor()) ? on : off));
291 Strip::notify_mute_changed ()
293 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
294 if (_route && _mute) {
295 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\troute muted ? %1\n", _route->muted()));
296 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_route->muted() ? on : off)));
298 _surface->write (_mute->set_state (_route->muted() ? on : off));
303 Strip::notify_record_enable_changed ()
305 if (_route && _recenable) {
306 _surface->write (_recenable->set_state (_route->record_enabled() ? on : off));
311 Strip::notify_active_changed ()
313 _surface->mcp().refresh_current_bank();
317 Strip::notify_route_deleted ()
319 _surface->mcp().refresh_current_bank();
323 Strip::notify_gain_changed (bool force_update)
329 boost::shared_ptr<AutomationControl> ac = _route->gain_control();
333 /* doesn't seem possible but lets be safe */
337 /* track gain control could be on vpot or fader, depending in
341 if (_vpot->control() == ac) {
343 } else if (_fader->control() == ac) {
349 float gain_coefficient = ac->get_value();
350 float normalized_position = ac->internal_to_interface (gain_coefficient);
352 if (force_update || normalized_position != _last_gain_position_written) {
354 if (!control->in_use()) {
355 if (control == _vpot) {
356 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
358 _surface->write (_fader->set_position (normalized_position));
362 do_parameter_display (GainAutomation, gain_coefficient);
363 _last_gain_position_written = normalized_position;
368 Strip::notify_processor_changed (bool force_update)
373 Strip::notify_property_changed (const PropertyChange& what_changed)
375 if (!what_changed.contains (ARDOUR::Properties::name)) {
383 Strip::show_route_name ()
385 MackieControlProtocol::SubViewMode svm = _surface->mcp().subview_mode();
387 if (svm != MackieControlProtocol::None) {
388 /* subview mode is responsible for upper line */
392 string fullname = string();
394 // make sure first three strips get cleared of view mode
399 fullname = _route->name();
403 if (fullname.length() <= 6) {
406 line1 = PBD::short_version (fullname, 6);
409 pending_display[0] = line1;
413 Strip::notify_send_level_change (AutomationType type, uint32_t send_num, bool force_update)
415 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
418 /* not in subview mode */
422 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
423 /* no longer in Sends subview mode */
427 boost::shared_ptr<AutomationControl> control = r->send_level_controllable (send_num);
433 float val = control->get_value();
434 do_parameter_display (type, val);
436 if (_vpot->control() == control) {
437 /* update pot/encoder */
438 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
444 Strip::notify_trackview_change (AutomationType type, uint32_t send_num, bool force_update)
446 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
449 /* not in subview mode */
453 if (_surface->mcp().subview_mode() != MackieControlProtocol::TrackView) {
454 /* no longer in TrackViewsubview mode */
458 boost::shared_ptr<AutomationControl> control;
459 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
463 control = r->trim_control();
465 case SoloIsolateAutomation:
466 control = r->solo_isolate_control ();
468 case SoloSafeAutomation:
469 control = r->solo_safe_control ();
471 case MonitoringAutomation:
473 control = track->monitoring_control();
476 case PhaseAutomation:
477 control = r->phase_control ();
484 float val = control->get_value();
486 /* Note: all of the displayed controllables require the display
487 * of their *actual* ("internal") value, not the version mapped
488 * into the normalized 0..1.0 ("interface") range.
491 do_parameter_display (type, val);
492 /* update pot/encoder */
493 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
498 Strip::notify_eq_change (AutomationType type, uint32_t band, bool force_update)
500 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
503 /* not in subview mode */
507 if (_surface->mcp().subview_mode() != MackieControlProtocol::EQ) {
508 /* no longer in EQ subview mode */
512 boost::shared_ptr<AutomationControl> control;
516 control = r->eq_gain_controllable (band);
519 control = r->eq_freq_controllable (band);
522 control = r->eq_q_controllable (band);
525 control = r->eq_shape_controllable (band);
528 control = r->eq_hpf_controllable ();
531 control = r->eq_enable_controllable ();
538 float val = control->get_value();
539 do_parameter_display (type, val);
540 /* update pot/encoder */
541 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
546 Strip::notify_dyn_change (AutomationType type, bool force_update, bool propagate_mode)
548 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
551 /* not in subview mode */
555 if (_surface->mcp().subview_mode() != MackieControlProtocol::Dynamics) {
556 /* no longer in EQ subview mode */
560 boost::shared_ptr<AutomationControl> control;
561 bool reset_all = false;
565 control = r->comp_threshold_controllable ();
568 control = r->comp_speed_controllable ();
571 control = r->comp_mode_controllable ();
575 control = r->comp_makeup_controllable ();
578 control = r->comp_redux_controllable ();
581 control = r->comp_enable_controllable ();
587 if (propagate_mode && reset_all) {
588 _surface->subview_mode_changed ();
592 float val = control->get_value();
593 do_parameter_display (type, val);
594 /* update pot/encoder */
595 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
600 Strip::notify_panner_azi_changed (bool force_update)
606 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
608 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
611 /* basically impossible, since we're here because that control
612 * changed, but sure, whatever.
617 if (_vpot->control() != pan_control) {
621 double normalized_pos = pan_control->internal_to_interface (pan_control->get_value());
622 double internal_pos = pan_control->get_value();
624 if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
626 _surface->write (_vpot->set (normalized_pos, true, Pot::dot));
627 /* show actual internal value to user */
628 do_parameter_display (PanAzimuthAutomation, internal_pos);
630 _last_pan_azi_position_written = normalized_pos;
635 Strip::notify_panner_width_changed (bool force_update)
641 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
643 boost::shared_ptr<AutomationControl> pan_control = _route->pan_width_control ();
646 /* basically impossible, since we're here because that control
647 * changed, but sure, whatever.
652 if (_vpot->control() != pan_control) {
656 double pos = pan_control->internal_to_interface (pan_control->get_value());
658 if (force_update || pos != _last_pan_width_position_written) {
660 _surface->write (_vpot->set (pos, true, Pot::spread));
661 do_parameter_display (PanWidthAutomation, pos);
663 _last_pan_width_position_written = pos;
668 Strip::select_event (Button&, ButtonState bs)
670 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
674 int ms = _surface->mcp().main_modifier_state();
676 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
677 _controls_locked = !_controls_locked;
678 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
679 block_vpot_mode_display_for (1000);
683 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
684 _surface->mcp().add_down_select_button (_surface->number(), _index);
685 _surface->mcp().select_range ();
688 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
689 _surface->mcp().remove_down_select_button (_surface->number(), _index);
694 Strip::vselect_event (Button&, ButtonState bs)
696 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
698 /* most subview modes: vpot press acts like a button for toggle parameters */
704 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
706 boost::shared_ptr<AutomationControl> control = _vpot->control ();
711 Controllable::GroupControlDisposition gcd;
712 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
713 gcd = Controllable::InverseGroup;
715 gcd = Controllable::UseGroup;
718 if (control->toggled()) {
719 if (control->toggled()) {
720 control->set_value (!control->get_value(), gcd);
723 } else if (control->desc().enumeration || control->desc().integer_step) {
725 double val = control->get_value ();
726 if (val <= control->upper() - 1.0) {
727 control->set_value (val + 1.0, gcd);
729 control->set_value (control->lower(), gcd);
735 /* Send mode: press enables/disables the relevant
736 * send, but the vpot is bound to the send-level so we
737 * need to lookup the enable/disable control
741 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
745 const uint32_t global_pos = _surface->mcp().global_index (*this);
746 boost::shared_ptr<AutomationControl> control = r->send_enable_controllable (global_pos);
749 bool currently_enabled = (bool) control->get_value();
750 Controllable::GroupControlDisposition gcd;
752 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
753 gcd = Controllable::InverseGroup;
755 gcd = Controllable::UseGroup;
758 control->set_value (!currently_enabled, gcd);
760 if (currently_enabled) {
761 /* we just turned it off */
762 pending_display[1] = "off";
764 /* we just turned it on, show the level
766 control = _route->send_level_controllable (global_pos);
767 do_parameter_display (BusSendLevel, control->get_value());
773 /* done with this event in subview mode */
780 int ms = _surface->mcp().main_modifier_state();
782 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
784 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
788 /* reset to default/normal value */
789 ac->set_value (ac->normal(), Controllable::NoGroup);
796 boost::shared_ptr<AutomationControl> ac = _route->master_send_enable_controllable ();
798 Controllable::GroupControlDisposition gcd;
800 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
801 gcd = Controllable::InverseGroup;
803 gcd = Controllable::UseGroup;
806 bool enabled = ac->get_value();
807 ac->set_value (!enabled, gcd);
811 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
812 /* switch vpot to control next available parameter */
821 Strip::fader_touch_event (Button&, ButtonState bs)
823 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
827 boost::shared_ptr<AutomationControl> ac = _fader->control ();
829 _fader->set_in_use (true);
830 _fader->start_touch (_surface->mcp().transport_frame());
833 do_parameter_display ((AutomationType) ac->parameter().type(), ac->get_value());
838 _fader->set_in_use (false);
839 _fader->stop_touch (_surface->mcp().transport_frame(), true);
846 Strip::handle_button (Button& button, ButtonState bs)
848 boost::shared_ptr<AutomationControl> control;
851 button.set_in_use (true);
853 button.set_in_use (false);
856 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
858 switch (button.bid()) {
860 select_event (button, bs);
863 case Button::VSelect:
864 vselect_event (button, bs);
867 case Button::FaderTouch:
868 fader_touch_event (button, bs);
872 if ((control = button.control ())) {
874 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
875 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
877 float new_value = control->get_value() ? 0.0 : 1.0;
879 /* get all controls that either have their
880 * button down or are within a range of
881 * several down buttons
884 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type());
887 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
888 controls.size(), control->parameter().type(), new_value));
890 /* apply change, with potential modifier semantics */
892 Controllable::GroupControlDisposition gcd;
894 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
895 gcd = Controllable::InverseGroup;
897 gcd = Controllable::UseGroup;
900 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
901 (*c)->set_value (new_value, gcd);
905 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
906 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
914 Strip::do_parameter_display (AutomationType type, float val)
916 bool screen_hold = false;
923 pending_display[1] = " -inf ";
925 float dB = accurate_coefficient_to_dB (val);
926 snprintf (buf, sizeof (buf), "%6.1f", dB);
927 pending_display[1] = buf;
932 case PanAzimuthAutomation:
933 if (Profile->get_mixbus()) {
934 snprintf (buf, sizeof (buf), "%2.1f", val);
935 pending_display[1] = buf;
939 boost::shared_ptr<Pannable> p = _route->pannable();
940 if (p && _route->panner()) {
941 pending_display[1] =_route->panner()->value_as_string (p->pan_azimuth_control);
948 case PanWidthAutomation:
950 snprintf (buf, sizeof (buf), "%5ld%%", lrintf ((val * 200.0)-100));
951 pending_display[1] = buf;
958 float dB = accurate_coefficient_to_dB (val);
959 snprintf (buf, sizeof (buf), "%6.1f", dB);
960 pending_display[1] = buf;
965 case PhaseAutomation:
968 pending_display[1] = "Normal";
970 pending_display[1] = "Invert";
985 snprintf (buf, sizeof (buf), "%6.1f", val);
986 pending_display[1] = buf;
992 pending_display[1] = "on";
994 pending_display[1] = "off";
998 if (_surface->mcp().subview_route()) {
999 pending_display[1] = _surface->mcp().subview_route()->comp_mode_name (val);
1002 case SoloSafeAutomation:
1003 case SoloIsolateAutomation:
1005 pending_display[1] = "on";
1007 pending_display[1] = "off";
1010 case MonitoringAutomation:
1011 switch (MonitorChoice ((int) val)) {
1013 pending_display[1] = "auto";
1016 pending_display[1] = "input";
1019 pending_display[1] = "disk";
1021 case MonitorCue: /* XXX not implemented as of jan 2016 */
1022 pending_display[1] = "cue";
1031 /* we just queued up a parameter to be displayed.
1032 1 second from now, switch back to vpot mode display.
1034 block_vpot_mode_display_for (1000);
1039 Strip::handle_fader_touch (Fader& fader, bool touch_on)
1042 fader.start_touch (_surface->mcp().transport_frame());
1044 fader.stop_touch (_surface->mcp().transport_frame(), false);
1049 Strip::handle_fader (Fader& fader, float position)
1051 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
1052 boost::shared_ptr<AutomationControl> ac = fader.control();
1057 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
1059 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1060 gcd = Controllable::InverseGroup;
1063 fader.set_value (position, gcd);
1065 /* From the Mackie Control MIDI implementation docs:
1067 In order to ensure absolute synchronization with the host software,
1068 Mackie Control uses a closed-loop servo system for the faders,
1069 meaning the faders will always move to their last received position.
1070 When a host receives a Fader Position Message, it must then
1071 re-transmit that message to the Mackie Control or else the faders
1072 will return to their last position.
1075 _surface->write (fader.set_position (position));
1079 Strip::handle_pot (Pot& pot, float delta)
1081 /* Pots only emit events when they move, not when they
1082 stop moving. So to get a stop event, we need to use a timeout.
1085 boost::shared_ptr<AutomationControl> ac = pot.control();
1090 Controllable::GroupControlDisposition gcd;
1092 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1093 gcd = Controllable::InverseGroup;
1095 gcd = Controllable::UseGroup;
1098 if (ac->toggled()) {
1100 /* make it like a single-step, directional switch */
1103 ac->set_value (1.0, gcd);
1105 ac->set_value (0.0, gcd);
1108 } else if (ac->desc().enumeration || ac->desc().integer_step) {
1110 /* use Controllable::get_value() to avoid the
1111 * "scaling-to-interface" that takes place in
1112 * Control::get_value() via the pot member.
1114 * an enumeration with 4 values will have interface values of
1115 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
1120 ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
1122 ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
1127 double p = ac->get_interface();
1134 ac->set_value ( ac->interface_to_internal(p), gcd);
1139 Strip::periodic (ARDOUR::microseconds_t now)
1142 update_automation ();
1146 Strip::redisplay (ARDOUR::microseconds_t now, bool force)
1148 if (_block_screen_redisplay_until >= now) {
1149 /* no drawing allowed */
1153 if (_block_screen_redisplay_until) {
1154 /* we were blocked, but the time period has elapsed, so we must
1158 _block_screen_redisplay_until = 0;
1161 if (force || (current_display[0] != pending_display[0])) {
1162 _surface->write (display (0, pending_display[0]));
1163 current_display[0] = pending_display[0];
1166 if (return_to_vpot_mode_display_at <= now) {
1167 return_to_vpot_mode_display_at = UINT64_MAX;
1168 return_to_vpot_mode_display ();
1171 if (force || (current_display[1] != pending_display[1])) {
1172 _surface->write (display (1, pending_display[1]));
1173 current_display[1] = pending_display[1];
1178 Strip::update_automation ()
1184 ARDOUR::AutoState state = _route->gain_control()->automation_state();
1186 if (state == Touch || state == Play) {
1187 notify_gain_changed (false);
1190 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
1192 state = pan_control->automation_state ();
1193 if (state == Touch || state == Play) {
1194 notify_panner_azi_changed (false);
1198 pan_control = _route->pan_width_control ();
1200 state = pan_control->automation_state ();
1201 if (state == Touch || state == Play) {
1202 notify_panner_width_changed (false);
1208 Strip::update_meter ()
1214 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1218 if (_meter && _transport_is_rolling && _metering_active) {
1219 float dB = const_cast<PeakMeter&> (_route->peak_meter()).meter_level (0, MeterMCP);
1220 _meter->send_update (*_surface, dB);
1228 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
1229 _surface->write ((*it)->zero ());
1232 _surface->write (blank_display (0));
1233 _surface->write (blank_display (1));
1234 pending_display[0] = string();
1235 pending_display[1] = string();
1236 current_display[0] = string();
1237 current_display[1] = string();
1241 Strip::blank_display (uint32_t line_number)
1243 return display (line_number, string());
1247 Strip::display (uint32_t line_number, const std::string& line)
1249 assert (line_number <= 1);
1251 MidiByteArray retval;
1253 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
1256 retval << _surface->sysex_hdr();
1260 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
1261 retval << (_index * 7 + (line_number * 0x38));
1263 // ascii data to display. @param line is UTF-8
1264 string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
1265 string::size_type len = ascii.length();
1267 ascii = ascii.substr (0, 6);
1271 // pad with " " out to 6 chars
1272 for (int i = len; i < 6; ++i) {
1276 // column spacer, unless it's the right-hand column
1282 retval << MIDI::eox;
1288 Strip::lock_controls ()
1290 _controls_locked = true;
1294 Strip::unlock_controls ()
1296 _controls_locked = false;
1300 Strip::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& rl)
1302 for (ARDOUR::StrongRouteNotificationList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
1303 if ((*i) == _route) {
1304 _surface->write (_select->set_state (on));
1309 _surface->write (_select->set_state (off));
1313 Strip::vpot_mode_string ()
1315 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1319 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1325 switch (ac->desc().type) {
1326 case PanAzimuthAutomation:
1328 case PanWidthAutomation:
1330 case PanElevationAutomation:
1332 case PanFrontBackAutomation:
1334 case PanLFEAutomation:
1344 Strip::flip_mode_changed ()
1346 if (_surface->mcp().subview_mode() == MackieControlProtocol::Sends) {
1348 boost::shared_ptr<AutomationControl> pot_control = _vpot->control();
1349 boost::shared_ptr<AutomationControl> fader_control = _fader->control();
1351 if (pot_control && fader_control) {
1352 _vpot->set_control (fader_control);
1353 _fader->set_control (pot_control);
1356 if (_surface->mcp().flip_mode() == MackieControlProtocol::Normal) {
1357 do_parameter_display (GainAutomation, fader_control->get_value());
1359 do_parameter_display (BusSendLevel, fader_control->get_value());
1364 _surface->write (_fader->set_position (pot_control->internal_to_interface (pot_control->get_value ())));
1368 _surface->write (_vpot->set (fader_control->internal_to_interface (fader_control->get_value()), true, Pot::wrap));
1377 Strip::block_screen_display_for (uint32_t msecs)
1379 _block_screen_redisplay_until = ARDOUR::get_microseconds() + (msecs * 1000);
1383 Strip::block_vpot_mode_display_for (uint32_t msecs)
1385 return_to_vpot_mode_display_at = ARDOUR::get_microseconds() + (msecs * 1000);
1389 Strip::return_to_vpot_mode_display ()
1391 /* returns the second line of the two-line per-strip display
1392 back the mode where it shows what the VPot controls.
1395 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1396 /* do nothing - second line shows value of current subview parameter */
1398 } else if (_route) {
1399 pending_display[1] = vpot_mode_string();
1401 pending_display[1] = string();
1406 Strip::next_pot_mode ()
1408 vector<AutomationType>::iterator i;
1410 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1411 /* do not change vpot mode while in flipped mode */
1412 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1413 pending_display[1] = "Flip";
1414 block_vpot_mode_display_for (1000);
1419 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1426 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1430 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1434 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1435 if ((*i) == ac->parameter().type()) {
1440 /* move to the next mode in the list, or back to the start (which will
1441 also happen if the current mode is not in the current pot mode list)
1444 if (i != possible_pot_parameters.end()) {
1448 if (i == possible_pot_parameters.end()) {
1449 i = possible_pot_parameters.begin();
1452 set_vpot_parameter (*i);
1456 Strip::subview_mode_changed ()
1458 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
1460 subview_connections.drop_connections ();
1462 switch (_surface->mcp().subview_mode()) {
1463 case MackieControlProtocol::None:
1464 set_vpot_parameter (_pan_mode);
1465 /* need to show strip name again */
1467 notify_metering_state_changed ();
1471 case MackieControlProtocol::EQ:
1475 /* leave it as it was */
1479 case MackieControlProtocol::Dynamics:
1483 /* leave it as it was */
1488 case MackieControlProtocol::Sends:
1490 setup_sends_vpot (r);
1492 /* leave it as it was */
1496 case MackieControlProtocol::TrackView:
1498 setup_trackview_vpot (r);
1500 /* leave it as it was */
1508 Strip::setup_dyn_vpot (boost::shared_ptr<Route> r)
1514 boost::shared_ptr<AutomationControl> tc = r->comp_threshold_controllable ();
1515 boost::shared_ptr<AutomationControl> sc = r->comp_speed_controllable ();
1516 boost::shared_ptr<AutomationControl> mc = r->comp_mode_controllable ();
1517 boost::shared_ptr<AutomationControl> kc = r->comp_makeup_controllable ();
1518 boost::shared_ptr<AutomationControl> rc = r->comp_redux_controllable ();
1519 boost::shared_ptr<AutomationControl> ec = r->comp_enable_controllable ();
1521 uint32_t pos = _surface->mcp().global_index (*this);
1523 /* we will control the pos-th available parameter, from the list in the
1524 * order shown above.
1527 vector<boost::shared_ptr<AutomationControl> > available;
1528 vector<AutomationType> params;
1530 if (tc) { available.push_back (tc); params.push_back (CompThreshold); }
1531 if (sc) { available.push_back (sc); params.push_back (CompSpeed); }
1532 if (mc) { available.push_back (mc); params.push_back (CompMode); }
1533 if (kc) { available.push_back (kc); params.push_back (CompMakeup); }
1534 if (rc) { available.push_back (rc); params.push_back (CompRedux); }
1535 if (ec) { available.push_back (ec); params.push_back (CompEnable); }
1537 if (pos >= available.size()) {
1538 /* this knob is not needed to control the available parameters */
1539 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1540 pending_display[0] = string();
1541 pending_display[1] = string();
1545 boost::shared_ptr<AutomationControl> pc;
1546 AutomationType param;
1548 pc = available[pos];
1549 param = params[pos];
1551 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_dyn_change, this, param, false, true), ui_context());
1552 _vpot->set_control (pc);
1562 pot_id = r->comp_speed_name (mc->get_value());
1583 if (!pot_id.empty()) {
1584 pending_display[0] = pot_id;
1586 pending_display[0] = string();
1589 notify_dyn_change (param, true, false);
1593 Strip::setup_eq_vpot (boost::shared_ptr<Route> r)
1595 uint32_t bands = r->eq_band_cnt ();
1598 /* should never get here */
1602 /* figure out how many params per band are available */
1604 boost::shared_ptr<AutomationControl> pc;
1605 uint32_t params_per_band = 0;
1607 if ((pc = r->eq_gain_controllable (0))) {
1608 params_per_band += 1;
1610 if ((pc = r->eq_freq_controllable (0))) {
1611 params_per_band += 1;
1613 if ((pc = r->eq_q_controllable (0))) {
1614 params_per_band += 1;
1616 if ((pc = r->eq_shape_controllable (0))) {
1617 params_per_band += 1;
1620 /* pick the one for this strip, based on its global position across
1626 const uint32_t total_band_parameters = bands * params_per_band;
1627 const uint32_t global_pos = _surface->mcp().global_index (*this);
1628 AutomationType param = NullAutomation;
1633 if (global_pos < total_band_parameters) {
1635 /* show a parameter for an EQ band */
1637 const uint32_t parameter = global_pos % params_per_band;
1638 eq_band = global_pos / params_per_band;
1639 band_name = r->eq_band_name (eq_band);
1641 switch (parameter) {
1643 pc = r->eq_gain_controllable (eq_band);
1647 pc = r->eq_freq_controllable (eq_band);
1648 param = EQFrequency;
1651 pc = r->eq_q_controllable (eq_band);
1655 pc = r->eq_shape_controllable (eq_band);
1662 /* show a non-band parameter (HPF or enable)
1665 uint32_t parameter = global_pos - total_band_parameters;
1667 switch (parameter) {
1668 case 0: /* first control after band parameters */
1669 pc = r->eq_hpf_controllable();
1672 case 1: /* second control after band parameters */
1673 pc = r->eq_enable_controllable();
1677 /* nothing to control */
1678 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1679 pending_display[0] = string();
1680 pending_display[1] = string();
1689 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_eq_change, this, param, eq_band, false), ui_context());
1690 _vpot->set_control (pc);
1696 pot_id = band_name + "Gain";
1699 pot_id = band_name + "Freq";
1702 pot_id = band_name + " Q";
1705 pot_id = band_name + " Shp";
1717 if (!pot_id.empty()) {
1718 pending_display[0] = pot_id;
1720 pending_display[0] = string();
1723 notify_eq_change (param, eq_band, true);
1728 Strip::setup_sends_vpot (boost::shared_ptr<Route> r)
1734 const uint32_t global_pos = _surface->mcp().global_index (*this);
1736 boost::shared_ptr<AutomationControl> pc = r->send_level_controllable (global_pos);
1739 pending_display[0] = string();
1740 pending_display[1] = string();
1744 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_send_level_change, this, BusSendLevel, global_pos, false), ui_context());
1745 _vpot->set_control (pc);
1747 pending_display[0] = r->send_name (global_pos);
1749 notify_send_level_change (BusSendLevel, global_pos, true);
1753 Strip::setup_trackview_vpot (boost::shared_ptr<Route> r)
1759 const uint32_t global_pos = _surface->mcp().global_index (*this);
1761 if (global_pos >= 8) {
1762 pending_display[0] = string();
1763 pending_display[1] = string();
1767 boost::shared_ptr<AutomationControl> pc;
1768 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
1771 switch (global_pos) {
1773 pc = r->trim_control ();
1775 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, TrimAutomation, global_pos, false), ui_context());
1776 pending_display[0] = "Trim";
1777 notify_trackview_change (TrimAutomation, global_pos, true);
1782 pc = track->monitoring_control();
1784 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, MonitoringAutomation, global_pos, false), ui_context());
1785 pending_display[0] = "Mon";
1786 notify_trackview_change (MonitoringAutomation, global_pos, true);
1791 pc = r->solo_isolate_control ();
1793 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloIsolateAutomation, global_pos, false), ui_context());
1794 notify_trackview_change (SoloIsolateAutomation, global_pos, true);
1795 pending_display[0] = "S-Iso";
1799 pc = r->solo_safe_control ();
1801 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloSafeAutomation, global_pos, false), ui_context());
1802 notify_trackview_change (SoloSafeAutomation, global_pos, true);
1803 pending_display[0] = "S-Safe";
1807 pc = r->phase_control();
1809 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, PhaseAutomation, global_pos, false), ui_context());
1810 notify_trackview_change (PhaseAutomation, global_pos, true);
1811 pending_display[0] = "Phase";
1815 // pc = r->trim_control ();
1818 // pc = r->trim_control ();
1821 // pc = r->trim_control ();
1826 pending_display[0] = string();
1827 pending_display[1] = string();
1831 _vpot->set_control (pc);
1835 Strip::set_vpot_parameter (AutomationType p)
1837 if (!_route || (p == NullAutomation)) {
1838 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1839 pending_display[1] = string();
1843 boost::shared_ptr<AutomationControl> pan_control;
1845 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1847 reset_saved_values ();
1850 case PanAzimuthAutomation:
1851 pan_control = _route->pan_azimuth_control ();
1853 case PanWidthAutomation:
1854 pan_control = _route->pan_width_control ();
1856 case PanElevationAutomation:
1858 case PanFrontBackAutomation:
1860 case PanLFEAutomation:
1868 _vpot->set_control (pan_control);
1871 pending_display[1] = vpot_mode_string ();
1875 Strip::is_midi_track () const
1877 return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1881 Strip::reset_saved_values ()
1883 _last_pan_azi_position_written = -1.0;
1884 _last_pan_width_position_written = -1.0;
1885 _last_gain_position_written = -1.0;
1886 _last_trim_position_written = -1.0;
1891 Strip::notify_metering_state_changed()
1893 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1897 if (!_route || !_meter) {
1901 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1902 bool metering_active = _surface->mcp().metering_active ();
1904 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1908 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1910 if (!transport_is_rolling || !metering_active) {
1911 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1912 notify_panner_azi_changed (true);
1915 _transport_is_rolling = transport_is_rolling;
1916 _metering_active = metering_active;