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/monitor_control.h"
41 #include "ardour/plugin_insert.h"
42 #include "ardour/pannable.h"
43 #include "ardour/panner.h"
44 #include "ardour/panner_shell.h"
45 #include "ardour/phase_control.h"
46 #include "ardour/rc_configuration.h"
47 #include "ardour/record_enable_control.h"
48 #include "ardour/route.h"
49 #include "ardour/session.h"
50 #include "ardour/send.h"
51 #include "ardour/solo_isolate_control.h"
52 #include "ardour/track.h"
53 #include "ardour/midi_track.h"
54 #include "ardour/user_bundle.h"
55 #include "ardour/profile.h"
57 #include "mackie_control_protocol.h"
58 #include "surface_port.h"
69 using namespace ARDOUR;
71 using namespace ArdourSurface;
72 using namespace Mackie;
74 #ifndef timeradd /// only avail with __USE_BSD
75 #define timeradd(a,b,result) \
77 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
78 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
79 if ((result)->tv_usec >= 1000000) \
82 (result)->tv_usec -= 1000000; \
87 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
89 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
102 , _controls_locked (false)
103 , _transport_is_rolling (false)
104 , _metering_active (true)
105 , _block_screen_redisplay_until (0)
106 , return_to_vpot_mode_display_at (UINT64_MAX)
108 , _pan_mode (PanAzimuthAutomation)
109 , _last_gain_position_written (-1.0)
110 , _last_pan_azi_position_written (-1.0)
111 , _last_pan_width_position_written (-1.0)
112 , _last_trim_position_written (-1.0)
114 _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
115 _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
117 if (s.mcp().device_info().has_meters()) {
118 _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
121 for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
122 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
123 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
124 _surface->number(), index, Button::id_to_name (bb->bid()),
125 bb->id(), b->second.base_id));
131 /* surface is responsible for deleting all controls */
135 Strip::add (Control & control)
139 Group::add (control);
141 /* fader, vpot, meter were all set explicitly */
143 if ((button = dynamic_cast<Button*>(&control)) != 0) {
144 switch (button->bid()) {
145 case Button::RecEnable:
157 case Button::VSelect:
160 case Button::FaderTouch:
161 _fader_touch = button;
170 Strip::set_stripable (boost::shared_ptr<Stripable> r, bool /*with_messages*/)
172 if (_controls_locked) {
176 mb_pan_controllable.reset();
178 stripable_connections.drop_connections ();
180 _solo->set_control (boost::shared_ptr<AutomationControl>());
181 _mute->set_control (boost::shared_ptr<AutomationControl>());
182 _select->set_control (boost::shared_ptr<AutomationControl>());
183 _recenable->set_control (boost::shared_ptr<AutomationControl>());
184 _fader->set_control (boost::shared_ptr<AutomationControl>());
185 _vpot->set_control (boost::shared_ptr<AutomationControl>());
189 reset_saved_values ();
192 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 Strip %2 mapped to null route\n", _surface->number(), _index));
197 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping stripable %3\n",
198 _surface->number(), _index, _stripable->name()));
200 _solo->set_control (_stripable->solo_control());
201 _mute->set_control (_stripable->mute_control());
203 _stripable->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
204 _stripable->mute_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
206 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control();
208 pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
211 pan_control = _stripable->pan_width_control();
213 pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
216 _stripable->gain_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
217 _stripable->PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
218 _stripable->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
220 boost::shared_ptr<AutomationControl> rec_enable_control = _stripable->rec_enable_control ();
222 if (rec_enable_control) {
223 _recenable->set_control (rec_enable_control);
224 rec_enable_control->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
227 // TODO this works when a currently-banked stripable is made inactive, but not
228 // when a stripable is activated which should be currently banked.
230 _stripable->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_stripable_deleted, this), ui_context());
232 /* setup legal VPot modes for this stripable */
234 possible_pot_parameters.clear();
236 if (_stripable->pan_azimuth_control()) {
237 possible_pot_parameters.push_back (PanAzimuthAutomation);
239 if (_stripable->pan_width_control()) {
240 possible_pot_parameters.push_back (PanWidthAutomation);
242 if (_stripable->pan_elevation_control()) {
243 possible_pot_parameters.push_back (PanElevationAutomation);
245 if (_stripable->pan_frontback_control()) {
246 possible_pot_parameters.push_back (PanFrontBackAutomation);
248 if (_stripable->pan_lfe_control()) {
249 possible_pot_parameters.push_back (PanLFEAutomation);
252 _pan_mode = PanAzimuthAutomation;
254 if (_surface->mcp().subview_mode() == MackieControlProtocol::None) {
255 set_vpot_parameter (_pan_mode);
258 _fader->set_control (_stripable->gain_control());
270 // The active V-pot control may not be active for this strip
271 // But if we zero it in the controls function it may erase
272 // the one we do want
273 _surface->write (_vpot->zero());
275 notify_solo_changed ();
276 notify_mute_changed ();
277 notify_gain_changed ();
278 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
279 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::selected));
280 notify_panner_azi_changed ();
281 notify_panner_width_changed ();
282 notify_record_enable_changed ();
283 notify_processor_changed ();
287 Strip::notify_solo_changed ()
289 if (_stripable && _solo) {
290 _surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
295 Strip::notify_mute_changed ()
297 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
298 if (_stripable && _mute) {
299 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\tstripable muted ? %1\n", _stripable->mute_control()->muted()));
300 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_stripable->mute_control()->muted() ? on : off)));
302 _surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
307 Strip::notify_record_enable_changed ()
309 if (_stripable && _recenable) {
310 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (_stripable);
312 _surface->write (_recenable->set_state (trk->rec_enable_control()->get_value() ? on : off));
318 Strip::notify_stripable_deleted ()
320 _surface->mcp().notify_stripable_removed ();
321 _surface->mcp().refresh_current_bank();
325 Strip::notify_gain_changed (bool force_update)
331 boost::shared_ptr<AutomationControl> ac = _stripable->gain_control();
335 /* doesn't seem possible but lets be safe */
339 /* track gain control could be on vpot or fader, depending in
343 if (_vpot->control() == ac) {
345 } else if (_fader->control() == ac) {
351 float gain_coefficient = ac->get_value();
352 float normalized_position = ac->internal_to_interface (gain_coefficient);
354 if (force_update || normalized_position != _last_gain_position_written) {
356 if (!control->in_use()) {
357 if (control == _vpot) {
358 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
360 _surface->write (_fader->set_position (normalized_position));
364 do_parameter_display (GainAutomation, gain_coefficient);
365 _last_gain_position_written = normalized_position;
370 Strip::notify_processor_changed (bool force_update)
375 Strip::notify_property_changed (const PropertyChange& what_changed)
377 if (what_changed.contains (ARDOUR::Properties::name)) {
378 show_stripable_name ();
381 if (what_changed.contains (ARDOUR::Properties::selected)) {
383 _surface->write (_select->set_state (_stripable->presentation_info().selected()));
384 _surface->mcp().update_selected (_stripable, _stripable->presentation_info().selected());
390 Strip::show_stripable_name ()
392 MackieControlProtocol::SubViewMode svm = _surface->mcp().subview_mode();
394 if (svm != MackieControlProtocol::None) {
395 /* subview mode is responsible for upper line */
399 string fullname = string();
403 fullname = _stripable->name();
406 if (fullname.length() <= 6) {
407 pending_display[0] = fullname;
409 pending_display[0] = PBD::short_version (fullname, 6);
414 Strip::notify_send_level_change (AutomationType type, uint32_t send_num, bool force_update)
416 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
419 /* not in subview mode */
423 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
424 /* no longer in Sends subview mode */
428 boost::shared_ptr<AutomationControl> control = r->send_level_controllable (send_num);
434 float val = control->get_value();
435 do_parameter_display (type, val);
437 if (_vpot->control() == control) {
438 /* update pot/encoder */
439 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
445 Strip::notify_trackview_change (AutomationType type, uint32_t send_num, bool force_update)
447 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
450 /* not in subview mode */
454 if (_surface->mcp().subview_mode() != MackieControlProtocol::TrackView) {
455 /* no longer in TrackViewsubview mode */
459 boost::shared_ptr<AutomationControl> control;
460 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
464 control = r->trim_control();
466 case SoloIsolateAutomation:
467 control = r->solo_isolate_control ();
469 case SoloSafeAutomation:
470 control = r->solo_safe_control ();
472 case MonitoringAutomation:
474 control = track->monitoring_control();
477 case PhaseAutomation:
478 control = r->phase_control ();
485 float val = control->get_value();
487 /* Note: all of the displayed controllables require the display
488 * of their *actual* ("internal") value, not the version mapped
489 * into the normalized 0..1.0 ("interface") range.
492 do_parameter_display (type, val);
493 /* update pot/encoder */
494 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
499 Strip::notify_eq_change (AutomationType type, uint32_t band, bool force_update)
501 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
504 /* not in subview mode */
508 if (_surface->mcp().subview_mode() != MackieControlProtocol::EQ) {
509 /* no longer in EQ subview mode */
513 boost::shared_ptr<AutomationControl> control;
517 control = r->eq_gain_controllable (band);
520 control = r->eq_freq_controllable (band);
523 control = r->eq_q_controllable (band);
526 control = r->eq_shape_controllable (band);
529 control = r->eq_hpf_controllable ();
532 control = r->eq_enable_controllable ();
539 float val = control->get_value();
540 do_parameter_display (type, val);
541 /* update pot/encoder */
542 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
547 Strip::notify_dyn_change (AutomationType type, bool force_update, bool propagate_mode)
549 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
552 /* not in subview mode */
556 if (_surface->mcp().subview_mode() != MackieControlProtocol::Dynamics) {
557 /* no longer in EQ subview mode */
561 boost::shared_ptr<AutomationControl> control;
562 bool reset_all = false;
566 control = r->comp_threshold_controllable ();
569 control = r->comp_speed_controllable ();
572 control = r->comp_mode_controllable ();
576 control = r->comp_makeup_controllable ();
579 control = r->comp_redux_controllable ();
582 control = r->comp_enable_controllable ();
588 if (propagate_mode && reset_all) {
589 _surface->subview_mode_changed ();
593 float val = control->get_value();
594 do_parameter_display (type, val);
595 /* update pot/encoder */
596 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
601 Strip::notify_panner_azi_changed (bool force_update)
607 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
609 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
612 /* basically impossible, since we're here because that control
613 * changed, but sure, whatever.
618 if (_vpot->control() != pan_control) {
622 double normalized_pos = pan_control->internal_to_interface (pan_control->get_value());
623 double internal_pos = pan_control->get_value();
625 if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
627 _surface->write (_vpot->set (normalized_pos, true, Pot::dot));
628 /* show actual internal value to user */
629 do_parameter_display (PanAzimuthAutomation, internal_pos);
631 _last_pan_azi_position_written = normalized_pos;
636 Strip::notify_panner_width_changed (bool force_update)
642 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
644 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_width_control ();
647 /* basically impossible, since we're here because that control
648 * changed, but sure, whatever.
653 if (_vpot->control() != pan_control) {
657 double pos = pan_control->internal_to_interface (pan_control->get_value());
659 if (force_update || pos != _last_pan_width_position_written) {
661 _surface->write (_vpot->set (pos, true, Pot::spread));
662 do_parameter_display (PanWidthAutomation, pos);
664 _last_pan_width_position_written = pos;
669 Strip::select_event (Button&, ButtonState bs)
671 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
675 int ms = _surface->mcp().main_modifier_state();
677 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
678 _controls_locked = !_controls_locked;
679 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
680 block_vpot_mode_display_for (1000);
684 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
685 _surface->mcp().add_down_select_button (_surface->number(), _index);
686 _surface->mcp().select_range (_surface->mcp().global_index (*this));
689 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
690 _surface->mcp().remove_down_select_button (_surface->number(), _index);
695 Strip::vselect_event (Button&, ButtonState bs)
697 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
699 /* most subview modes: vpot press acts like a button for toggle parameters */
705 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
707 boost::shared_ptr<AutomationControl> control = _vpot->control ();
712 Controllable::GroupControlDisposition gcd;
713 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
714 gcd = Controllable::InverseGroup;
716 gcd = Controllable::UseGroup;
719 if (control->toggled()) {
720 if (control->toggled()) {
721 control->set_value (!control->get_value(), gcd);
724 } else if (control->desc().enumeration || control->desc().integer_step) {
726 double val = control->get_value ();
727 if (val <= control->upper() - 1.0) {
728 control->set_value (val + 1.0, gcd);
730 control->set_value (control->lower(), gcd);
736 /* Send mode: press enables/disables the relevant
737 * send, but the vpot is bound to the send-level so we
738 * need to lookup the enable/disable control
742 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
746 const uint32_t global_pos = _surface->mcp().global_index (*this);
747 boost::shared_ptr<AutomationControl> control = r->send_enable_controllable (global_pos);
750 bool currently_enabled = (bool) control->get_value();
751 Controllable::GroupControlDisposition gcd;
753 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
754 gcd = Controllable::InverseGroup;
756 gcd = Controllable::UseGroup;
759 control->set_value (!currently_enabled, gcd);
761 if (currently_enabled) {
762 /* we just turned it off */
763 pending_display[1] = "off";
765 /* we just turned it on, show the level
767 control = _stripable->send_level_controllable (global_pos);
768 do_parameter_display (BusSendLevel, control->get_value());
774 /* done with this event in subview mode */
781 int ms = _surface->mcp().main_modifier_state();
783 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
785 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
789 /* reset to default/normal value */
790 ac->set_value (ac->normal(), Controllable::NoGroup);
797 boost::shared_ptr<AutomationControl> ac = _stripable->master_send_enable_controllable ();
799 Controllable::GroupControlDisposition gcd;
801 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
802 gcd = Controllable::InverseGroup;
804 gcd = Controllable::UseGroup;
807 bool enabled = ac->get_value();
808 ac->set_value (!enabled, gcd);
812 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
813 /* switch vpot to control next available parameter */
822 Strip::fader_touch_event (Button&, ButtonState bs)
824 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
828 boost::shared_ptr<AutomationControl> ac = _fader->control ();
830 _fader->set_in_use (true);
831 _fader->start_touch (_surface->mcp().transport_frame());
834 do_parameter_display ((AutomationType) ac->parameter().type(), ac->get_value());
839 _fader->set_in_use (false);
840 _fader->stop_touch (_surface->mcp().transport_frame(), true);
847 Strip::handle_button (Button& button, ButtonState bs)
849 boost::shared_ptr<AutomationControl> control;
852 button.set_in_use (true);
854 button.set_in_use (false);
857 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
859 switch (button.bid()) {
861 select_event (button, bs);
864 case Button::VSelect:
865 vselect_event (button, bs);
868 case Button::FaderTouch:
869 fader_touch_event (button, bs);
873 if ((control = button.control ())) {
875 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
876 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
878 float new_value = control->get_value() ? 0.0 : 1.0;
880 /* get all controls that either have their
881 * button down or are within a range of
882 * several down buttons
885 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type(),
886 _surface->mcp().global_index(*this));
889 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
890 controls.size(), control->parameter().type(), new_value));
892 /* apply change, with potential modifier semantics */
894 Controllable::GroupControlDisposition gcd;
896 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
897 gcd = Controllable::InverseGroup;
899 gcd = Controllable::UseGroup;
902 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
903 (*c)->set_value (new_value, gcd);
907 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
908 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
916 Strip::do_parameter_display (AutomationType type, float val)
918 bool screen_hold = false;
924 pending_display[1] = " -inf ";
926 float dB = accurate_coefficient_to_dB (val);
927 snprintf (buf, sizeof (buf), "%6.1f", dB);
928 pending_display[1] = buf;
934 if (Profile->get_mixbus()) { //Mixbus sends are already stored in dB
935 snprintf (buf, sizeof (buf), "%2.1f", val);
936 pending_display[1] = buf;
940 pending_display[1] = " -inf ";
942 float dB = accurate_coefficient_to_dB (val);
943 snprintf (buf, sizeof (buf), "%6.1f", dB);
944 pending_display[1] = buf;
950 case PanAzimuthAutomation:
951 if (Profile->get_mixbus()) {
952 snprintf (buf, sizeof (buf), "%2.1f", val);
953 pending_display[1] = buf;
957 boost::shared_ptr<AutomationControl> pa = _stripable->pan_azimuth_control();
959 pending_display[1] = pa->get_user_string ();
966 case PanWidthAutomation:
968 snprintf (buf, sizeof (buf), "%5ld%%", lrintf ((val * 200.0)-100));
969 pending_display[1] = buf;
976 float dB = accurate_coefficient_to_dB (val);
977 snprintf (buf, sizeof (buf), "%6.1f", dB);
978 pending_display[1] = buf;
983 case PhaseAutomation:
986 pending_display[1] = "Normal";
988 pending_display[1] = "Invert";
1003 snprintf (buf, sizeof (buf), "%6.1f", val);
1004 pending_display[1] = buf;
1010 pending_display[1] = "on";
1012 pending_display[1] = "off";
1016 if (_surface->mcp().subview_stripable()) {
1017 pending_display[1] = _surface->mcp().subview_stripable()->comp_mode_name (val);
1020 case SoloSafeAutomation:
1021 case SoloIsolateAutomation:
1023 pending_display[1] = "on";
1025 pending_display[1] = "off";
1028 case MonitoringAutomation:
1029 switch (MonitorChoice ((int) val)) {
1031 pending_display[1] = "auto";
1034 pending_display[1] = "input";
1037 pending_display[1] = "disk";
1039 case MonitorCue: /* XXX not implemented as of jan 2016 */
1040 pending_display[1] = "cue";
1049 /* we just queued up a parameter to be displayed.
1050 1 second from now, switch back to vpot mode display.
1052 block_vpot_mode_display_for (1000);
1057 Strip::handle_fader_touch (Fader& fader, bool touch_on)
1060 fader.start_touch (_surface->mcp().transport_frame());
1062 fader.stop_touch (_surface->mcp().transport_frame(), false);
1067 Strip::handle_fader (Fader& fader, float position)
1069 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
1070 boost::shared_ptr<AutomationControl> ac = fader.control();
1075 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
1077 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1078 gcd = Controllable::InverseGroup;
1081 fader.set_value (position, gcd);
1083 /* From the Mackie Control MIDI implementation docs:
1085 In order to ensure absolute synchronization with the host software,
1086 Mackie Control uses a closed-loop servo system for the faders,
1087 meaning the faders will always move to their last received position.
1088 When a host receives a Fader Position Message, it must then
1089 re-transmit that message to the Mackie Control or else the faders
1090 will return to their last position.
1093 _surface->write (fader.set_position (position));
1097 Strip::handle_pot (Pot& pot, float delta)
1099 /* Pots only emit events when they move, not when they
1100 stop moving. So to get a stop event, we need to use a timeout.
1103 boost::shared_ptr<AutomationControl> ac = pot.control();
1108 Controllable::GroupControlDisposition gcd;
1110 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1111 gcd = Controllable::InverseGroup;
1113 gcd = Controllable::UseGroup;
1116 if (ac->toggled()) {
1118 /* make it like a single-step, directional switch */
1121 ac->set_value (1.0, gcd);
1123 ac->set_value (0.0, gcd);
1126 } else if (ac->desc().enumeration || ac->desc().integer_step) {
1128 /* use Controllable::get_value() to avoid the
1129 * "scaling-to-interface" that takes place in
1130 * Control::get_value() via the pot member.
1132 * an enumeration with 4 values will have interface values of
1133 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
1138 ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
1140 ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
1145 double p = ac->get_interface();
1152 ac->set_value ( ac->interface_to_internal(p), gcd);
1157 Strip::periodic (ARDOUR::microseconds_t now)
1160 update_automation ();
1164 Strip::redisplay (ARDOUR::microseconds_t now, bool force)
1166 if (_block_screen_redisplay_until >= now) {
1167 /* no drawing allowed */
1171 if (_block_screen_redisplay_until) {
1172 /* we were blocked, but the time period has elapsed, so we must
1176 _block_screen_redisplay_until = 0;
1179 if (force || (current_display[0] != pending_display[0])) {
1180 _surface->write (display (0, pending_display[0]));
1181 current_display[0] = pending_display[0];
1184 if (return_to_vpot_mode_display_at <= now) {
1185 return_to_vpot_mode_display_at = UINT64_MAX;
1186 return_to_vpot_mode_display ();
1189 if (force || (current_display[1] != pending_display[1])) {
1190 _surface->write (display (1, pending_display[1]));
1191 current_display[1] = pending_display[1];
1196 Strip::update_automation ()
1202 ARDOUR::AutoState state = _stripable->gain_control()->automation_state();
1204 if (state == Touch || state == Play) {
1205 notify_gain_changed (false);
1208 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
1210 state = pan_control->automation_state ();
1211 if (state == Touch || state == Play) {
1212 notify_panner_azi_changed (false);
1216 pan_control = _stripable->pan_width_control ();
1218 state = pan_control->automation_state ();
1219 if (state == Touch || state == Play) {
1220 notify_panner_width_changed (false);
1226 Strip::update_meter ()
1232 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1236 if (_meter && _transport_is_rolling && _metering_active && _stripable->peak_meter()) {
1237 float dB = _stripable->peak_meter()->meter_level (0, MeterMCP);
1238 _meter->send_update (*_surface, dB);
1246 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
1247 _surface->write ((*it)->zero ());
1250 _surface->write (blank_display (0));
1251 _surface->write (blank_display (1));
1252 pending_display[0] = string();
1253 pending_display[1] = string();
1254 current_display[0] = string();
1255 current_display[1] = string();
1259 Strip::blank_display (uint32_t line_number)
1261 return display (line_number, string());
1265 Strip::display (uint32_t line_number, const std::string& line)
1267 assert (line_number <= 1);
1269 MidiByteArray retval;
1271 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
1274 retval << _surface->sysex_hdr();
1278 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
1279 retval << (_index * 7 + (line_number * 0x38));
1281 // ascii data to display. @param line is UTF-8
1282 string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
1283 string::size_type len = ascii.length();
1285 ascii = ascii.substr (0, 6);
1289 // pad with " " out to 6 chars
1290 for (int i = len; i < 6; ++i) {
1294 // column spacer, unless it's the right-hand column
1300 retval << MIDI::eox;
1306 Strip::lock_controls ()
1308 _controls_locked = true;
1312 Strip::unlock_controls ()
1314 _controls_locked = false;
1318 Strip::vpot_mode_string ()
1320 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1324 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1330 switch (ac->desc().type) {
1331 case PanAzimuthAutomation:
1333 case PanWidthAutomation:
1335 case PanElevationAutomation:
1337 case PanFrontBackAutomation:
1339 case PanLFEAutomation:
1345 //"None" mode, by definition (currently) shows the pan control above the fader.
1346 //Mixbus controllers are created from a LADSPA so they don't have ac->desc().type
1347 //For the forseeable future, we will just return "Pan" here.
1355 Strip::flip_mode_changed ()
1357 if (_surface->mcp().subview_mode() == MackieControlProtocol::Sends) {
1359 boost::shared_ptr<AutomationControl> pot_control = _vpot->control();
1360 boost::shared_ptr<AutomationControl> fader_control = _fader->control();
1362 if (pot_control && fader_control) {
1364 _vpot->set_control (fader_control);
1365 _fader->set_control (pot_control);
1367 /* update fader with pot value */
1369 _surface->write (_fader->set_position (pot_control->internal_to_interface (pot_control->get_value ())));
1371 /* update pot with fader value */
1373 _surface->write (_vpot->set (fader_control->internal_to_interface (fader_control->get_value()), true, Pot::wrap));
1376 if (_surface->mcp().flip_mode() == MackieControlProtocol::Normal) {
1377 do_parameter_display (GainAutomation, fader_control->get_value());
1379 do_parameter_display (BusSendLevel, pot_control->get_value());
1390 Strip::block_screen_display_for (uint32_t msecs)
1392 _block_screen_redisplay_until = ARDOUR::get_microseconds() + (msecs * 1000);
1396 Strip::block_vpot_mode_display_for (uint32_t msecs)
1398 return_to_vpot_mode_display_at = ARDOUR::get_microseconds() + (msecs * 1000);
1402 Strip::return_to_vpot_mode_display ()
1404 /* returns the second line of the two-line per-strip display
1405 back the mode where it shows what the VPot controls.
1408 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1409 /* do nothing - second line shows value of current subview parameter */
1411 } else if (_stripable) {
1412 pending_display[1] = vpot_mode_string();
1414 pending_display[1] = string();
1419 Strip::next_pot_mode ()
1421 vector<AutomationType>::iterator i;
1423 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1424 /* do not change vpot mode while in flipped mode */
1425 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1426 pending_display[1] = "Flip";
1427 block_vpot_mode_display_for (1000);
1432 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1439 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1443 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1447 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1448 if ((*i) == ac->parameter().type()) {
1453 /* move to the next mode in the list, or back to the start (which will
1454 also happen if the current mode is not in the current pot mode list)
1457 if (i != possible_pot_parameters.end()) {
1461 if (i == possible_pot_parameters.end()) {
1462 i = possible_pot_parameters.begin();
1465 set_vpot_parameter (*i);
1469 Strip::subview_mode_changed ()
1471 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
1473 subview_connections.drop_connections ();
1475 switch (_surface->mcp().subview_mode()) {
1476 case MackieControlProtocol::None:
1477 set_vpot_parameter (_pan_mode);
1478 /* need to show strip name again */
1479 show_stripable_name ();
1481 _surface->write (_vpot->set (0, true, Pot::wrap));
1482 _surface->write (_fader->set_position (0.0));
1484 notify_metering_state_changed ();
1488 case MackieControlProtocol::EQ:
1492 /* leave it as it was */
1496 case MackieControlProtocol::Dynamics:
1500 /* leave it as it was */
1505 case MackieControlProtocol::Sends:
1507 setup_sends_vpot (r);
1509 /* leave it as it was */
1513 case MackieControlProtocol::TrackView:
1515 setup_trackview_vpot (r);
1517 /* leave it as it was */
1525 Strip::setup_dyn_vpot (boost::shared_ptr<Stripable> r)
1531 boost::shared_ptr<AutomationControl> tc = r->comp_threshold_controllable ();
1532 boost::shared_ptr<AutomationControl> sc = r->comp_speed_controllable ();
1533 boost::shared_ptr<AutomationControl> mc = r->comp_mode_controllable ();
1534 boost::shared_ptr<AutomationControl> kc = r->comp_makeup_controllable ();
1535 boost::shared_ptr<AutomationControl> rc = r->comp_redux_controllable ();
1536 boost::shared_ptr<AutomationControl> ec = r->comp_enable_controllable ();
1538 uint32_t pos = _surface->mcp().global_index (*this);
1540 /* we will control the pos-th available parameter, from the list in the
1541 * order shown above.
1544 vector<boost::shared_ptr<AutomationControl> > available;
1545 vector<AutomationType> params;
1547 if (tc) { available.push_back (tc); params.push_back (CompThreshold); }
1548 if (sc) { available.push_back (sc); params.push_back (CompSpeed); }
1549 if (mc) { available.push_back (mc); params.push_back (CompMode); }
1550 if (kc) { available.push_back (kc); params.push_back (CompMakeup); }
1551 if (rc) { available.push_back (rc); params.push_back (CompRedux); }
1552 if (ec) { available.push_back (ec); params.push_back (CompEnable); }
1554 if (pos >= available.size()) {
1555 /* this knob is not needed to control the available parameters */
1556 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1557 pending_display[0] = string();
1558 pending_display[1] = string();
1562 boost::shared_ptr<AutomationControl> pc;
1563 AutomationType param;
1565 pc = available[pos];
1566 param = params[pos];
1568 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_dyn_change, this, param, false, true), ui_context());
1569 _vpot->set_control (pc);
1579 pot_id = r->comp_speed_name (mc->get_value());
1600 if (!pot_id.empty()) {
1601 pending_display[0] = pot_id;
1603 pending_display[0] = string();
1606 notify_dyn_change (param, true, false);
1610 Strip::setup_eq_vpot (boost::shared_ptr<Stripable> r)
1612 uint32_t bands = r->eq_band_cnt ();
1615 /* should never get here */
1619 /* figure out how many params per band are available */
1621 boost::shared_ptr<AutomationControl> pc;
1622 uint32_t params_per_band = 0;
1624 if ((pc = r->eq_gain_controllable (0))) {
1625 params_per_band += 1;
1627 if ((pc = r->eq_freq_controllable (0))) {
1628 params_per_band += 1;
1630 if ((pc = r->eq_q_controllable (0))) {
1631 params_per_band += 1;
1633 if ((pc = r->eq_shape_controllable (0))) {
1634 params_per_band += 1;
1637 /* pick the one for this strip, based on its global position across
1643 const uint32_t total_band_parameters = bands * params_per_band;
1644 const uint32_t global_pos = _surface->mcp().global_index (*this);
1645 AutomationType param = NullAutomation;
1650 if (global_pos < total_band_parameters) {
1652 /* show a parameter for an EQ band */
1654 const uint32_t parameter = global_pos % params_per_band;
1655 eq_band = global_pos / params_per_band;
1656 band_name = r->eq_band_name (eq_band);
1658 switch (parameter) {
1660 pc = r->eq_gain_controllable (eq_band);
1664 pc = r->eq_freq_controllable (eq_band);
1665 param = EQFrequency;
1668 pc = r->eq_q_controllable (eq_band);
1672 pc = r->eq_shape_controllable (eq_band);
1679 /* show a non-band parameter (HPF or enable)
1682 uint32_t parameter = global_pos - total_band_parameters;
1684 switch (parameter) {
1685 case 0: /* first control after band parameters */
1686 pc = r->eq_hpf_controllable();
1689 case 1: /* second control after band parameters */
1690 pc = r->eq_enable_controllable();
1694 /* nothing to control */
1695 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1696 pending_display[0] = string();
1697 pending_display[1] = string();
1706 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_eq_change, this, param, eq_band, false), ui_context());
1707 _vpot->set_control (pc);
1713 pot_id = band_name + "Gain";
1716 pot_id = band_name + "Freq";
1719 pot_id = band_name + " Q";
1722 pot_id = band_name + " Shp";
1734 if (!pot_id.empty()) {
1735 pending_display[0] = pot_id;
1737 pending_display[0] = string();
1740 notify_eq_change (param, eq_band, true);
1745 Strip::setup_sends_vpot (boost::shared_ptr<Stripable> r)
1751 const uint32_t global_pos = _surface->mcp().global_index (*this);
1753 boost::shared_ptr<AutomationControl> pc = r->send_level_controllable (global_pos);
1756 /* nothing to control */
1757 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1758 pending_display[0] = string();
1759 pending_display[1] = string();
1763 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_send_level_change, this, BusSendLevel, global_pos, false), ui_context());
1764 _vpot->set_control (pc);
1766 pending_display[0] = PBD::short_version (r->send_name (global_pos), 6);
1768 notify_send_level_change (BusSendLevel, global_pos, true);
1772 Strip::setup_trackview_vpot (boost::shared_ptr<Stripable> r)
1778 const uint32_t global_pos = _surface->mcp().global_index (*this);
1780 if (global_pos >= 8) {
1781 /* nothing to control */
1782 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1783 pending_display[0] = string();
1784 pending_display[1] = string();
1788 boost::shared_ptr<AutomationControl> pc;
1789 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
1792 switch (global_pos) {
1794 pc = r->trim_control ();
1796 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, TrimAutomation, global_pos, false), ui_context());
1797 pending_display[0] = "Trim";
1798 notify_trackview_change (TrimAutomation, global_pos, true);
1803 pc = track->monitoring_control();
1805 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, MonitoringAutomation, global_pos, false), ui_context());
1806 pending_display[0] = "Mon";
1807 notify_trackview_change (MonitoringAutomation, global_pos, true);
1812 pc = r->solo_isolate_control ();
1814 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloIsolateAutomation, global_pos, false), ui_context());
1815 notify_trackview_change (SoloIsolateAutomation, global_pos, true);
1816 pending_display[0] = "S-Iso";
1820 pc = r->solo_safe_control ();
1822 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloSafeAutomation, global_pos, false), ui_context());
1823 notify_trackview_change (SoloSafeAutomation, global_pos, true);
1824 pending_display[0] = "S-Safe";
1828 pc = r->phase_control();
1830 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, PhaseAutomation, global_pos, false), ui_context());
1831 notify_trackview_change (PhaseAutomation, global_pos, true);
1832 pending_display[0] = "Phase";
1836 // pc = r->trim_control ();
1839 // pc = r->trim_control ();
1842 // pc = r->trim_control ();
1847 pending_display[0] = string();
1848 pending_display[1] = string();
1852 _vpot->set_control (pc);
1856 Strip::set_vpot_parameter (AutomationType p)
1858 if (!_stripable || (p == NullAutomation)) {
1859 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1860 pending_display[1] = string();
1864 boost::shared_ptr<AutomationControl> pan_control;
1866 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1868 reset_saved_values ();
1871 case PanAzimuthAutomation:
1872 pan_control = _stripable->pan_azimuth_control ();
1874 case PanWidthAutomation:
1875 pan_control = _stripable->pan_width_control ();
1877 case PanElevationAutomation:
1879 case PanFrontBackAutomation:
1881 case PanLFEAutomation:
1889 _vpot->set_control (pan_control);
1892 pending_display[1] = vpot_mode_string ();
1896 Strip::is_midi_track () const
1898 return boost::dynamic_pointer_cast<MidiTrack>(_stripable) != 0;
1902 Strip::reset_saved_values ()
1904 _last_pan_azi_position_written = -1.0;
1905 _last_pan_width_position_written = -1.0;
1906 _last_gain_position_written = -1.0;
1907 _last_trim_position_written = -1.0;
1912 Strip::notify_metering_state_changed()
1914 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1918 if (!_stripable || !_meter) {
1922 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1923 bool metering_active = _surface->mcp().metering_active ();
1925 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1929 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1931 if (!transport_is_rolling || !metering_active) {
1932 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1933 notify_panner_azi_changed (true);
1936 _transport_is_rolling = transport_is_rolling;
1937 _metering_active = metering_active;