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->is_selected()));
384 _surface->mcp().update_selected (_stripable, _stripable->is_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_enable_controllable ();
533 control = r->eq_hpf_controllable ();
541 float val = control->get_value();
542 do_parameter_display (type, val);
543 /* update pot/encoder */
544 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
549 Strip::notify_dyn_change (AutomationType type, bool force_update, bool propagate_mode)
551 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
554 /* not in subview mode */
558 if (_surface->mcp().subview_mode() != MackieControlProtocol::Dynamics) {
559 /* no longer in EQ subview mode */
563 boost::shared_ptr<AutomationControl> control;
564 bool reset_all = false;
568 control = r->comp_threshold_controllable ();
571 control = r->comp_speed_controllable ();
574 control = r->comp_mode_controllable ();
578 control = r->comp_makeup_controllable ();
581 control = r->comp_enable_controllable ();
585 control = r->eq_hpf_controllable ();
588 control = r->eq_lpf_controllable ();
591 control = r->filter_enable_controllable ();
598 if (propagate_mode && reset_all) {
599 _surface->subview_mode_changed ();
603 float val = control->get_value();
604 do_parameter_display (type, val);
605 /* update pot/encoder */
606 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
611 Strip::notify_panner_azi_changed (bool force_update)
617 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
619 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
622 /* basically impossible, since we're here because that control
623 * changed, but sure, whatever.
628 if (_vpot->control() != pan_control) {
632 double normalized_pos = pan_control->internal_to_interface (pan_control->get_value());
633 double internal_pos = pan_control->get_value();
635 if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
637 _surface->write (_vpot->set (normalized_pos, true, Pot::dot));
638 /* show actual internal value to user */
639 do_parameter_display (PanAzimuthAutomation, internal_pos);
641 _last_pan_azi_position_written = normalized_pos;
646 Strip::notify_panner_width_changed (bool force_update)
652 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
654 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_width_control ();
657 /* basically impossible, since we're here because that control
658 * changed, but sure, whatever.
663 if (_vpot->control() != pan_control) {
667 double pos = pan_control->internal_to_interface (pan_control->get_value());
669 if (force_update || pos != _last_pan_width_position_written) {
671 _surface->write (_vpot->set (pos, true, Pot::spread));
672 do_parameter_display (PanWidthAutomation, pos);
674 _last_pan_width_position_written = pos;
679 Strip::select_event (Button&, ButtonState bs)
681 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
685 int ms = _surface->mcp().main_modifier_state();
687 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
688 _controls_locked = !_controls_locked;
689 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
690 block_vpot_mode_display_for (1000);
694 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
695 _surface->mcp().add_down_select_button (_surface->number(), _index);
696 _surface->mcp().select_range (_surface->mcp().global_index (*this));
699 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
700 _surface->mcp().remove_down_select_button (_surface->number(), _index);
705 Strip::vselect_event (Button&, ButtonState bs)
707 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
709 /* most subview modes: vpot press acts like a button for toggle parameters */
715 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
717 boost::shared_ptr<AutomationControl> control = _vpot->control ();
722 Controllable::GroupControlDisposition gcd;
723 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
724 gcd = Controllable::InverseGroup;
726 gcd = Controllable::UseGroup;
729 if (control->toggled()) {
730 if (control->toggled()) {
731 control->set_value (!control->get_value(), gcd);
734 } else if (control->desc().enumeration || control->desc().integer_step) {
736 double val = control->get_value ();
737 if (val <= control->upper() - 1.0) {
738 control->set_value (val + 1.0, gcd);
740 control->set_value (control->lower(), gcd);
746 /* Send mode: press enables/disables the relevant
747 * send, but the vpot is bound to the send-level so we
748 * need to lookup the enable/disable control
752 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
756 const uint32_t global_pos = _surface->mcp().global_index (*this);
757 boost::shared_ptr<AutomationControl> control = r->send_enable_controllable (global_pos);
760 bool currently_enabled = (bool) control->get_value();
761 Controllable::GroupControlDisposition gcd;
763 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
764 gcd = Controllable::InverseGroup;
766 gcd = Controllable::UseGroup;
769 control->set_value (!currently_enabled, gcd);
771 if (currently_enabled) {
772 /* we just turned it off */
773 pending_display[1] = "off";
775 /* we just turned it on, show the level
777 control = _stripable->send_level_controllable (global_pos);
778 do_parameter_display (BusSendLevel, control->get_value());
784 /* done with this event in subview mode */
791 int ms = _surface->mcp().main_modifier_state();
793 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
795 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
799 /* reset to default/normal value */
800 ac->set_value (ac->normal(), Controllable::NoGroup);
807 boost::shared_ptr<AutomationControl> ac = _stripable->master_send_enable_controllable ();
809 Controllable::GroupControlDisposition gcd;
811 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
812 gcd = Controllable::InverseGroup;
814 gcd = Controllable::UseGroup;
817 bool enabled = ac->get_value();
818 ac->set_value (!enabled, gcd);
822 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
823 /* switch vpot to control next available parameter */
832 Strip::fader_touch_event (Button&, ButtonState bs)
834 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
838 boost::shared_ptr<AutomationControl> ac = _fader->control ();
840 _fader->set_in_use (true);
841 _fader->start_touch (_surface->mcp().transport_frame());
844 do_parameter_display ((AutomationType) ac->parameter().type(), ac->get_value());
849 _fader->set_in_use (false);
850 _fader->stop_touch (_surface->mcp().transport_frame(), true);
857 Strip::handle_button (Button& button, ButtonState bs)
859 boost::shared_ptr<AutomationControl> control;
862 button.set_in_use (true);
864 button.set_in_use (false);
867 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
869 switch (button.bid()) {
871 select_event (button, bs);
874 case Button::VSelect:
875 vselect_event (button, bs);
878 case Button::FaderTouch:
879 fader_touch_event (button, bs);
883 if ((control = button.control ())) {
885 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
886 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
888 float new_value = control->get_value() ? 0.0 : 1.0;
890 /* get all controls that either have their
891 * button down or are within a range of
892 * several down buttons
895 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type(),
896 _surface->mcp().global_index(*this));
899 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
900 controls.size(), control->parameter().type(), new_value));
902 /* apply change, with potential modifier semantics */
904 Controllable::GroupControlDisposition gcd;
906 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
907 gcd = Controllable::InverseGroup;
909 gcd = Controllable::UseGroup;
912 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
913 (*c)->set_value (new_value, gcd);
917 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
918 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
926 Strip::do_parameter_display (AutomationType type, float val)
928 bool screen_hold = false;
934 pending_display[1] = " -inf ";
936 float dB = accurate_coefficient_to_dB (val);
937 snprintf (buf, sizeof (buf), "%6.1f", dB);
938 pending_display[1] = buf;
944 if (Profile->get_mixbus()) { //Mixbus sends are already stored in dB
945 snprintf (buf, sizeof (buf), "%2.1f", val);
946 pending_display[1] = buf;
950 pending_display[1] = " -inf ";
952 float dB = accurate_coefficient_to_dB (val);
953 snprintf (buf, sizeof (buf), "%6.1f", dB);
954 pending_display[1] = buf;
960 case PanAzimuthAutomation:
961 if (Profile->get_mixbus()) {
962 snprintf (buf, sizeof (buf), "%2.1f", val);
963 pending_display[1] = buf;
967 boost::shared_ptr<AutomationControl> pa = _stripable->pan_azimuth_control();
969 pending_display[1] = pa->get_user_string ();
976 case PanWidthAutomation:
978 snprintf (buf, sizeof (buf), "%5ld%%", lrintf ((val * 200.0)-100));
979 pending_display[1] = buf;
986 float dB = accurate_coefficient_to_dB (val);
987 snprintf (buf, sizeof (buf), "%6.1f", dB);
988 pending_display[1] = buf;
993 case PhaseAutomation:
996 pending_display[1] = "Normal";
998 pending_display[1] = "Invert";
1014 snprintf (buf, sizeof (buf), "%6.1f", val);
1015 pending_display[1] = buf;
1018 case EQFilterEnable:
1022 pending_display[1] = "on";
1024 pending_display[1] = "off";
1028 if (_surface->mcp().subview_stripable()) {
1029 pending_display[1] = _surface->mcp().subview_stripable()->comp_mode_name (val);
1032 case SoloSafeAutomation:
1033 case SoloIsolateAutomation:
1035 pending_display[1] = "on";
1037 pending_display[1] = "off";
1040 case MonitoringAutomation:
1041 switch (MonitorChoice ((int) val)) {
1043 pending_display[1] = "auto";
1046 pending_display[1] = "input";
1049 pending_display[1] = "disk";
1051 case MonitorCue: /* XXX not implemented as of jan 2016 */
1052 pending_display[1] = "cue";
1061 /* we just queued up a parameter to be displayed.
1062 1 second from now, switch back to vpot mode display.
1064 block_vpot_mode_display_for (1000);
1069 Strip::handle_fader_touch (Fader& fader, bool touch_on)
1072 fader.start_touch (_surface->mcp().transport_frame());
1074 fader.stop_touch (_surface->mcp().transport_frame(), false);
1079 Strip::handle_fader (Fader& fader, float position)
1081 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
1082 boost::shared_ptr<AutomationControl> ac = fader.control();
1087 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
1089 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1090 gcd = Controllable::InverseGroup;
1093 fader.set_value (position, gcd);
1095 /* From the Mackie Control MIDI implementation docs:
1097 In order to ensure absolute synchronization with the host software,
1098 Mackie Control uses a closed-loop servo system for the faders,
1099 meaning the faders will always move to their last received position.
1100 When a host receives a Fader Position Message, it must then
1101 re-transmit that message to the Mackie Control or else the faders
1102 will return to their last position.
1105 _surface->write (fader.set_position (position));
1109 Strip::handle_pot (Pot& pot, float delta)
1111 /* Pots only emit events when they move, not when they
1112 stop moving. So to get a stop event, we need to use a timeout.
1115 boost::shared_ptr<AutomationControl> ac = pot.control();
1120 Controllable::GroupControlDisposition gcd;
1122 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1123 gcd = Controllable::InverseGroup;
1125 gcd = Controllable::UseGroup;
1128 if (ac->toggled()) {
1130 /* make it like a single-step, directional switch */
1133 ac->set_value (1.0, gcd);
1135 ac->set_value (0.0, gcd);
1138 } else if (ac->desc().enumeration || ac->desc().integer_step) {
1140 /* use Controllable::get_value() to avoid the
1141 * "scaling-to-interface" that takes place in
1142 * Control::get_value() via the pot member.
1144 * an enumeration with 4 values will have interface values of
1145 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
1150 ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
1152 ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
1157 double p = ac->get_interface();
1164 ac->set_value ( ac->interface_to_internal(p), gcd);
1169 Strip::periodic (ARDOUR::microseconds_t now)
1172 update_automation ();
1176 Strip::redisplay (ARDOUR::microseconds_t now, bool force)
1178 if (_block_screen_redisplay_until >= now) {
1179 /* no drawing allowed */
1183 if (_block_screen_redisplay_until) {
1184 /* we were blocked, but the time period has elapsed, so we must
1188 _block_screen_redisplay_until = 0;
1191 if (force || (current_display[0] != pending_display[0])) {
1192 _surface->write (display (0, pending_display[0]));
1193 current_display[0] = pending_display[0];
1196 if (return_to_vpot_mode_display_at <= now) {
1197 return_to_vpot_mode_display_at = UINT64_MAX;
1198 return_to_vpot_mode_display ();
1201 if (force || (current_display[1] != pending_display[1])) {
1202 _surface->write (display (1, pending_display[1]));
1203 current_display[1] = pending_display[1];
1208 Strip::update_automation ()
1214 ARDOUR::AutoState state = _stripable->gain_control()->automation_state();
1216 if (state == Touch || state == Play) {
1217 notify_gain_changed (false);
1220 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
1222 state = pan_control->automation_state ();
1223 if (state == Touch || state == Play) {
1224 notify_panner_azi_changed (false);
1228 pan_control = _stripable->pan_width_control ();
1230 state = pan_control->automation_state ();
1231 if (state == Touch || state == Play) {
1232 notify_panner_width_changed (false);
1238 Strip::update_meter ()
1244 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1248 if (_meter && _transport_is_rolling && _metering_active && _stripable->peak_meter()) {
1249 float dB = _stripable->peak_meter()->meter_level (0, MeterMCP);
1250 _meter->send_update (*_surface, dB);
1258 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
1259 _surface->write ((*it)->zero ());
1262 _surface->write (blank_display (0));
1263 _surface->write (blank_display (1));
1264 pending_display[0] = string();
1265 pending_display[1] = string();
1266 current_display[0] = string();
1267 current_display[1] = string();
1271 Strip::blank_display (uint32_t line_number)
1273 return display (line_number, string());
1277 Strip::display (uint32_t line_number, const std::string& line)
1279 assert (line_number <= 1);
1281 MidiByteArray retval;
1283 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
1286 retval << _surface->sysex_hdr();
1290 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
1291 retval << (_index * 7 + (line_number * 0x38));
1293 // ascii data to display. @param line is UTF-8
1294 string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
1295 string::size_type len = ascii.length();
1297 ascii = ascii.substr (0, 6);
1301 // pad with " " out to 6 chars
1302 for (int i = len; i < 6; ++i) {
1306 // column spacer, unless it's the right-hand column
1312 retval << MIDI::eox;
1318 Strip::lock_controls ()
1320 _controls_locked = true;
1324 Strip::unlock_controls ()
1326 _controls_locked = false;
1330 Strip::vpot_mode_string ()
1332 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1336 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1342 switch (ac->desc().type) {
1343 case PanAzimuthAutomation:
1345 case PanWidthAutomation:
1347 case PanElevationAutomation:
1349 case PanFrontBackAutomation:
1351 case PanLFEAutomation:
1357 //"None" mode, by definition (currently) shows the pan control above the fader.
1358 //Mixbus controllers are created from a LADSPA so they don't have ac->desc().type
1359 //For the forseeable future, we will just return "Pan" here.
1367 Strip::flip_mode_changed ()
1369 if (_surface->mcp().subview_mode() == MackieControlProtocol::Sends) {
1371 boost::shared_ptr<AutomationControl> pot_control = _vpot->control();
1372 boost::shared_ptr<AutomationControl> fader_control = _fader->control();
1374 if (pot_control && fader_control) {
1376 _vpot->set_control (fader_control);
1377 _fader->set_control (pot_control);
1379 /* update fader with pot value */
1381 _surface->write (_fader->set_position (pot_control->internal_to_interface (pot_control->get_value ())));
1383 /* update pot with fader value */
1385 _surface->write (_vpot->set (fader_control->internal_to_interface (fader_control->get_value()), true, Pot::wrap));
1388 if (_surface->mcp().flip_mode() == MackieControlProtocol::Normal) {
1389 do_parameter_display (GainAutomation, fader_control->get_value());
1391 do_parameter_display (BusSendLevel, pot_control->get_value());
1402 Strip::block_screen_display_for (uint32_t msecs)
1404 _block_screen_redisplay_until = ARDOUR::get_microseconds() + (msecs * 1000);
1408 Strip::block_vpot_mode_display_for (uint32_t msecs)
1410 return_to_vpot_mode_display_at = ARDOUR::get_microseconds() + (msecs * 1000);
1414 Strip::return_to_vpot_mode_display ()
1416 /* returns the second line of the two-line per-strip display
1417 back the mode where it shows what the VPot controls.
1420 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1421 /* do nothing - second line shows value of current subview parameter */
1423 } else if (_stripable) {
1424 pending_display[1] = vpot_mode_string();
1426 pending_display[1] = string();
1431 Strip::next_pot_mode ()
1433 vector<AutomationType>::iterator i;
1435 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1436 /* do not change vpot mode while in flipped mode */
1437 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1438 pending_display[1] = "Flip";
1439 block_vpot_mode_display_for (1000);
1444 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1451 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1455 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1459 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1460 if ((*i) == ac->parameter().type()) {
1465 /* move to the next mode in the list, or back to the start (which will
1466 also happen if the current mode is not in the current pot mode list)
1469 if (i != possible_pot_parameters.end()) {
1473 if (i == possible_pot_parameters.end()) {
1474 i = possible_pot_parameters.begin();
1477 set_vpot_parameter (*i);
1481 Strip::subview_mode_changed ()
1483 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
1485 subview_connections.drop_connections ();
1487 switch (_surface->mcp().subview_mode()) {
1488 case MackieControlProtocol::None:
1489 set_vpot_parameter (_pan_mode);
1490 /* need to show strip name again */
1491 show_stripable_name ();
1493 _surface->write (_vpot->set (0, true, Pot::wrap));
1494 _surface->write (_fader->set_position (0.0));
1496 notify_metering_state_changed ();
1500 case MackieControlProtocol::EQ:
1504 /* leave it as it was */
1508 case MackieControlProtocol::Dynamics:
1512 /* leave it as it was */
1517 case MackieControlProtocol::Sends:
1519 setup_sends_vpot (r);
1521 /* leave it as it was */
1525 case MackieControlProtocol::TrackView:
1527 setup_trackview_vpot (r);
1529 /* leave it as it was */
1537 Strip::setup_dyn_vpot (boost::shared_ptr<Stripable> r)
1543 boost::shared_ptr<AutomationControl> tc = r->comp_threshold_controllable ();
1544 boost::shared_ptr<AutomationControl> sc = r->comp_speed_controllable ();
1545 boost::shared_ptr<AutomationControl> mc = r->comp_mode_controllable ();
1546 boost::shared_ptr<AutomationControl> kc = r->comp_makeup_controllable ();
1547 boost::shared_ptr<AutomationControl> ec = r->comp_enable_controllable ();
1549 #ifdef MIXBUS32C //Mixbus32C needs to spill the filter controls into the comp section
1550 boost::shared_ptr<AutomationControl> hpfc = r->eq_hpf_controllable ();
1551 boost::shared_ptr<AutomationControl> lpfc = r->eq_lpf_controllable ();
1552 boost::shared_ptr<AutomationControl> fec = r->filter_enable_controllable ();
1555 uint32_t pos = _surface->mcp().global_index (*this);
1557 /* we will control the pos-th available parameter, from the list in the
1558 * order shown above.
1561 vector<boost::shared_ptr<AutomationControl> > available;
1562 vector<AutomationType> params;
1564 if (tc) { available.push_back (tc); params.push_back (CompThreshold); }
1565 if (sc) { available.push_back (sc); params.push_back (CompSpeed); }
1566 if (mc) { available.push_back (mc); params.push_back (CompMode); }
1567 if (kc) { available.push_back (kc); params.push_back (CompMakeup); }
1568 if (ec) { available.push_back (ec); params.push_back (CompEnable); }
1570 #ifdef MIXBUS32C //Mixbus32C needs to spill the filter controls into the comp section
1571 if (hpfc) { available.push_back (hpfc); params.push_back (EQHPF); }
1572 if (lpfc) { available.push_back (lpfc); params.push_back (EQLPF); }
1573 if (fec) { available.push_back (fec); params.push_back (EQFilterEnable); }
1576 if (pos >= available.size()) {
1577 /* this knob is not needed to control the available parameters */
1578 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1579 pending_display[0] = string();
1580 pending_display[1] = string();
1584 boost::shared_ptr<AutomationControl> pc;
1585 AutomationType param;
1587 pc = available[pos];
1588 param = params[pos];
1590 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_dyn_change, this, param, false, true), ui_context());
1591 _vpot->set_control (pc);
1601 pot_id = r->comp_speed_name (mc->get_value());
1625 case EQFilterEnable:
1637 if (!pot_id.empty()) {
1638 pending_display[0] = pot_id;
1640 pending_display[0] = string();
1643 notify_dyn_change (param, true, false);
1647 Strip::setup_eq_vpot (boost::shared_ptr<Stripable> r)
1649 uint32_t bands = r->eq_band_cnt ();
1652 /* should never get here */
1656 /* figure out how many params per band are available */
1658 boost::shared_ptr<AutomationControl> pc;
1659 uint32_t params_per_band = 0;
1661 if ((pc = r->eq_gain_controllable (0))) {
1662 params_per_band += 1;
1664 if ((pc = r->eq_freq_controllable (0))) {
1665 params_per_band += 1;
1667 if ((pc = r->eq_q_controllable (0))) {
1668 params_per_band += 1;
1670 if ((pc = r->eq_shape_controllable (0))) {
1671 params_per_band += 1;
1674 /* pick the one for this strip, based on its global position across
1680 const uint32_t total_band_parameters = bands * params_per_band;
1681 const uint32_t global_pos = _surface->mcp().global_index (*this);
1682 AutomationType param = NullAutomation;
1687 if (global_pos < total_band_parameters) {
1689 /* show a parameter for an EQ band */
1691 const uint32_t parameter = global_pos % params_per_band;
1692 eq_band = global_pos / params_per_band;
1693 band_name = r->eq_band_name (eq_band);
1695 switch (parameter) {
1696 #ifdef MIXBUS32C //in 32C, we swap the order of freq/gain to match the GUI
1698 pc = r->eq_freq_controllable (eq_band);
1699 param = EQFrequency;
1702 pc = r->eq_gain_controllable (eq_band);
1707 pc = r->eq_gain_controllable (eq_band);
1711 pc = r->eq_freq_controllable (eq_band);
1712 param = EQFrequency;
1716 pc = r->eq_q_controllable (eq_band);
1720 pc = r->eq_shape_controllable (eq_band);
1727 /* show a non-band parameter (HPF or enable)
1730 uint32_t parameter = global_pos - total_band_parameters;
1732 switch (parameter) {
1734 case 0: /* first control after band parameters */
1735 pc = r->eq_hpf_controllable();
1738 case 1: /* second control after band parameters */
1739 pc = r->eq_enable_controllable();
1744 /* nothing to control */
1745 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1746 pending_display[0] = string();
1747 pending_display[1] = string();
1756 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_eq_change, this, param, eq_band, false), ui_context());
1757 _vpot->set_control (pc);
1763 pot_id = band_name + "Gain";
1766 pot_id = band_name + "Freq";
1769 pot_id = band_name + " Q";
1772 pot_id = band_name + " Shp";
1786 if (!pot_id.empty()) {
1787 pending_display[0] = pot_id;
1789 pending_display[0] = string();
1792 notify_eq_change (param, eq_band, true);
1797 Strip::setup_sends_vpot (boost::shared_ptr<Stripable> r)
1803 const uint32_t global_pos = _surface->mcp().global_index (*this);
1805 boost::shared_ptr<AutomationControl> pc = r->send_level_controllable (global_pos);
1808 /* nothing to control */
1809 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1810 pending_display[0] = string();
1811 pending_display[1] = string();
1815 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_send_level_change, this, BusSendLevel, global_pos, false), ui_context());
1816 _vpot->set_control (pc);
1818 pending_display[0] = PBD::short_version (r->send_name (global_pos), 6);
1820 notify_send_level_change (BusSendLevel, global_pos, true);
1824 Strip::setup_trackview_vpot (boost::shared_ptr<Stripable> r)
1830 const uint32_t global_pos = _surface->mcp().global_index (*this);
1832 if (global_pos >= 8) {
1833 /* nothing to control */
1834 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1835 pending_display[0] = string();
1836 pending_display[1] = string();
1840 boost::shared_ptr<AutomationControl> pc;
1841 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
1844 switch (global_pos) {
1846 pc = r->trim_control ();
1848 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, TrimAutomation, global_pos, false), ui_context());
1849 pending_display[0] = "Trim";
1850 notify_trackview_change (TrimAutomation, global_pos, true);
1855 pc = track->monitoring_control();
1857 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, MonitoringAutomation, global_pos, false), ui_context());
1858 pending_display[0] = "Mon";
1859 notify_trackview_change (MonitoringAutomation, global_pos, true);
1864 pc = r->solo_isolate_control ();
1866 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloIsolateAutomation, global_pos, false), ui_context());
1867 notify_trackview_change (SoloIsolateAutomation, global_pos, true);
1868 pending_display[0] = "S-Iso";
1872 pc = r->solo_safe_control ();
1874 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloSafeAutomation, global_pos, false), ui_context());
1875 notify_trackview_change (SoloSafeAutomation, global_pos, true);
1876 pending_display[0] = "S-Safe";
1880 pc = r->phase_control();
1882 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, PhaseAutomation, global_pos, false), ui_context());
1883 notify_trackview_change (PhaseAutomation, global_pos, true);
1884 pending_display[0] = "Phase";
1888 // pc = r->trim_control ();
1891 // pc = r->trim_control ();
1894 // pc = r->trim_control ();
1899 pending_display[0] = string();
1900 pending_display[1] = string();
1904 _vpot->set_control (pc);
1908 Strip::set_vpot_parameter (AutomationType p)
1910 if (!_stripable || (p == NullAutomation)) {
1911 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1912 pending_display[1] = string();
1916 boost::shared_ptr<AutomationControl> pan_control;
1918 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1920 reset_saved_values ();
1923 case PanAzimuthAutomation:
1924 pan_control = _stripable->pan_azimuth_control ();
1926 case PanWidthAutomation:
1927 pan_control = _stripable->pan_width_control ();
1929 case PanElevationAutomation:
1931 case PanFrontBackAutomation:
1933 case PanLFEAutomation:
1941 _vpot->set_control (pan_control);
1944 pending_display[1] = vpot_mode_string ();
1948 Strip::is_midi_track () const
1950 return boost::dynamic_pointer_cast<MidiTrack>(_stripable) != 0;
1954 Strip::reset_saved_values ()
1956 _last_pan_azi_position_written = -1.0;
1957 _last_pan_width_position_written = -1.0;
1958 _last_gain_position_written = -1.0;
1959 _last_trim_position_written = -1.0;
1964 Strip::notify_metering_state_changed()
1966 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1970 if (!_stripable || !_meter) {
1974 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1975 bool metering_active = _surface->mcp().metering_active ();
1977 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1981 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1983 if (!transport_is_rolling || !metering_active) {
1984 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1985 notify_panner_azi_changed (true);
1988 _transport_is_rolling = transport_is_rolling;
1989 _metering_active = metering_active;