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"
56 #include "ardour/value_as_string.h"
58 #include "mackie_control_protocol.h"
59 #include "surface_port.h"
70 using namespace ARDOUR;
72 using namespace ArdourSurface;
73 using namespace Mackie;
75 #ifndef timeradd /// only avail with __USE_BSD
76 #define timeradd(a,b,result) \
78 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
79 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
80 if ((result)->tv_usec >= 1000000) \
83 (result)->tv_usec -= 1000000; \
88 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
90 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
103 , _controls_locked (false)
104 , _transport_is_rolling (false)
105 , _metering_active (true)
106 , _block_screen_redisplay_until (0)
107 , return_to_vpot_mode_display_at (UINT64_MAX)
109 , _pan_mode (PanAzimuthAutomation)
110 , _last_gain_position_written (-1.0)
111 , _last_pan_azi_position_written (-1.0)
112 , _last_pan_width_position_written (-1.0)
113 , _last_trim_position_written (-1.0)
115 _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
116 _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
118 if (s.mcp().device_info().has_meters()) {
119 _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
122 for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
123 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
124 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
125 _surface->number(), index, Button::id_to_name (bb->bid()),
126 bb->id(), b->second.base_id));
132 /* surface is responsible for deleting all controls */
136 Strip::add (Control & control)
140 Group::add (control);
142 /* fader, vpot, meter were all set explicitly */
144 if ((button = dynamic_cast<Button*>(&control)) != 0) {
145 switch (button->bid()) {
146 case Button::RecEnable:
158 case Button::VSelect:
161 case Button::FaderTouch:
162 _fader_touch = button;
171 Strip::set_stripable (boost::shared_ptr<Stripable> r, bool /*with_messages*/)
173 if (_controls_locked) {
177 mb_pan_controllable.reset();
179 stripable_connections.drop_connections ();
181 _solo->set_control (boost::shared_ptr<AutomationControl>());
182 _mute->set_control (boost::shared_ptr<AutomationControl>());
183 _select->set_control (boost::shared_ptr<AutomationControl>());
184 _recenable->set_control (boost::shared_ptr<AutomationControl>());
185 _fader->set_control (boost::shared_ptr<AutomationControl>());
186 _vpot->set_control (boost::shared_ptr<AutomationControl>());
190 reset_saved_values ();
193 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 Strip %2 mapped to null route\n", _surface->number(), _index));
198 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping stripable %3\n",
199 _surface->number(), _index, _stripable->name()));
201 _solo->set_control (_stripable->solo_control());
202 _mute->set_control (_stripable->mute_control());
204 _stripable->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
205 _stripable->mute_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
207 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control();
209 pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
212 pan_control = _stripable->pan_width_control();
214 pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
217 _stripable->gain_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
218 _stripable->PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
219 _stripable->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
221 boost::shared_ptr<AutomationControl> rec_enable_control = _stripable->rec_enable_control ();
223 if (rec_enable_control) {
224 _recenable->set_control (rec_enable_control);
225 rec_enable_control->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
228 // TODO this works when a currently-banked stripable is made inactive, but not
229 // when a stripable is activated which should be currently banked.
231 _stripable->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_stripable_deleted, this), ui_context());
233 /* setup legal VPot modes for this stripable */
235 possible_pot_parameters.clear();
237 if (_stripable->pan_azimuth_control()) {
238 possible_pot_parameters.push_back (PanAzimuthAutomation);
240 if (_stripable->pan_width_control()) {
241 possible_pot_parameters.push_back (PanWidthAutomation);
243 if (_stripable->pan_elevation_control()) {
244 possible_pot_parameters.push_back (PanElevationAutomation);
246 if (_stripable->pan_frontback_control()) {
247 possible_pot_parameters.push_back (PanFrontBackAutomation);
249 if (_stripable->pan_lfe_control()) {
250 possible_pot_parameters.push_back (PanLFEAutomation);
253 _pan_mode = PanAzimuthAutomation;
255 if (_surface->mcp().subview_mode() == MackieControlProtocol::None) {
256 set_vpot_parameter (_pan_mode);
259 _fader->set_control (_stripable->gain_control());
271 // The active V-pot control may not be active for this strip
272 // But if we zero it in the controls function it may erase
273 // the one we do want
274 _surface->write (_vpot->zero());
276 notify_solo_changed ();
277 notify_mute_changed ();
278 notify_gain_changed ();
279 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
280 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::selected));
281 notify_panner_azi_changed ();
282 notify_panner_width_changed ();
283 notify_record_enable_changed ();
284 notify_processor_changed ();
288 Strip::notify_solo_changed ()
290 if (_stripable && _solo) {
291 _surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
296 Strip::notify_mute_changed ()
298 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
299 if (_stripable && _mute) {
300 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\tstripable muted ? %1\n", _stripable->mute_control()->muted()));
301 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_stripable->mute_control()->muted() ? on : off)));
303 _surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
308 Strip::notify_record_enable_changed ()
310 if (_stripable && _recenable) {
311 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (_stripable);
313 _surface->write (_recenable->set_state (trk->rec_enable_control()->get_value() ? on : off));
319 Strip::notify_stripable_deleted ()
321 _surface->mcp().notify_stripable_removed ();
322 _surface->mcp().refresh_current_bank();
326 Strip::notify_gain_changed (bool force_update)
332 boost::shared_ptr<AutomationControl> ac = _stripable->gain_control();
336 /* doesn't seem possible but lets be safe */
340 /* track gain control could be on vpot or fader, depending in
344 if (_vpot->control() == ac) {
346 } else if (_fader->control() == ac) {
352 float gain_coefficient = ac->get_value();
353 float normalized_position = ac->internal_to_interface (gain_coefficient);
355 if (force_update || normalized_position != _last_gain_position_written) {
357 if (!control->in_use()) {
358 if (control == _vpot) {
359 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
361 _surface->write (_fader->set_position (normalized_position));
365 do_parameter_display (ac->desc(), gain_coefficient); // GainAutomation
366 _last_gain_position_written = normalized_position;
371 Strip::notify_processor_changed (bool force_update)
376 Strip::notify_property_changed (const PropertyChange& what_changed)
378 if (what_changed.contains (ARDOUR::Properties::name)) {
379 show_stripable_name ();
384 Strip::update_selection_state ()
387 _surface->write (_select->set_state (_stripable->is_selected()));
392 Strip::show_stripable_name ()
394 MackieControlProtocol::SubViewMode svm = _surface->mcp().subview_mode();
396 if (svm != MackieControlProtocol::None) {
397 /* subview mode is responsible for upper line */
401 string fullname = string();
405 fullname = _stripable->name();
408 if (fullname.length() <= 6) {
409 pending_display[0] = fullname;
411 pending_display[0] = PBD::short_version (fullname, 6);
416 Strip::notify_send_level_change (uint32_t send_num, bool force_update)
418 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
421 /* not in subview mode */
425 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
426 /* no longer in Sends subview mode */
430 boost::shared_ptr<AutomationControl> control = r->send_level_controllable (send_num);
436 float val = control->get_value();
437 do_parameter_display (control->desc (), val); // BusSendLevel
439 if (_vpot->control() == control) {
440 /* update pot/encoder */
441 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
447 Strip::notify_trackview_change (AutomationType type, uint32_t send_num, bool force_update)
449 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
452 /* not in subview mode */
456 if (_surface->mcp().subview_mode() != MackieControlProtocol::TrackView) {
457 /* no longer in TrackViewsubview mode */
461 boost::shared_ptr<AutomationControl> control;
462 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
463 bool screen_hold = false;
467 control = r->trim_control();
470 case SoloIsolateAutomation:
471 control = r->solo_isolate_control ();
473 case SoloSafeAutomation:
474 control = r->solo_safe_control ();
476 case MonitoringAutomation:
478 control = track->monitoring_control();
482 case PhaseAutomation:
483 control = r->phase_control ();
491 float val = control->get_value();
493 /* Note: all of the displayed controllables require the display
494 * of their *actual* ("internal") value, not the version mapped
495 * into the normalized 0..1.0 ("interface") range.
498 do_parameter_display (control->desc(), val, screen_hold);
499 /* update pot/encoder */
500 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
505 Strip::notify_eq_change (boost::weak_ptr<AutomationControl> pc, bool force_update)
507 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
510 /* not in subview mode */
514 if (_surface->mcp().subview_mode() != MackieControlProtocol::EQ) {
515 /* no longer in EQ subview mode */
519 boost::shared_ptr<AutomationControl> control = pc.lock ();
521 float val = control->get_value();
522 do_parameter_display (control->desc(), val, true);
523 /* update pot/encoder */
524 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
529 Strip::notify_dyn_change (boost::weak_ptr<AutomationControl> pc, bool force_update, bool propagate_mode)
531 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
534 /* not in subview mode */
538 if (_surface->mcp().subview_mode() != MackieControlProtocol::Dynamics) {
539 /* no longer in EQ subview mode */
543 boost::shared_ptr<AutomationControl> control= pc.lock ();
544 bool reset_all = false;
546 if (propagate_mode && reset_all) {
547 _surface->subview_mode_changed ();
551 float val = control->get_value();
552 if (control == r->comp_mode_controllable ()) {
553 pending_display[1] = r->comp_mode_name (val);
555 do_parameter_display (control->desc(), val, true);
557 /* update pot/encoder */
558 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
563 Strip::notify_panner_azi_changed (bool force_update)
569 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
571 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
574 /* basically impossible, since we're here because that control
575 * changed, but sure, whatever.
580 if (_vpot->control() != pan_control) {
584 double normalized_pos = pan_control->internal_to_interface (pan_control->get_value());
585 double internal_pos = pan_control->get_value();
587 if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
589 _surface->write (_vpot->set (normalized_pos, true, Pot::dot));
590 /* show actual internal value to user */
591 do_parameter_display (pan_control->desc(), internal_pos); // PanAzimuthAutomation
593 _last_pan_azi_position_written = normalized_pos;
598 Strip::notify_panner_width_changed (bool force_update)
604 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
606 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_width_control ();
609 /* basically impossible, since we're here because that control
610 * changed, but sure, whatever.
615 if (_vpot->control() != pan_control) {
619 double pos = pan_control->internal_to_interface (pan_control->get_value());
621 if (force_update || pos != _last_pan_width_position_written) {
623 _surface->write (_vpot->set (pos, true, Pot::spread));
624 do_parameter_display (pan_control->desc(), pos); // PanWidthAutomation
626 _last_pan_width_position_written = pos;
631 Strip::select_event (Button&, ButtonState bs)
633 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
637 int ms = _surface->mcp().main_modifier_state();
639 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
640 _controls_locked = !_controls_locked;
641 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
642 block_vpot_mode_display_for (1000);
646 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
647 _surface->mcp().add_down_select_button (_surface->number(), _index);
648 _surface->mcp().select_range (_surface->mcp().global_index (*this));
651 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
652 _surface->mcp().remove_down_select_button (_surface->number(), _index);
657 Strip::vselect_event (Button&, ButtonState bs)
659 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
661 /* most subview modes: vpot press acts like a button for toggle parameters */
667 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
669 boost::shared_ptr<AutomationControl> control = _vpot->control ();
674 Controllable::GroupControlDisposition gcd;
675 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
676 gcd = Controllable::InverseGroup;
678 gcd = Controllable::UseGroup;
681 if (control->toggled()) {
682 if (control->toggled()) {
683 control->set_value (!control->get_value(), gcd);
686 } else if (control->desc().enumeration || control->desc().integer_step) {
688 double val = control->get_value ();
689 if (val <= control->upper() - 1.0) {
690 control->set_value (val + 1.0, gcd);
692 control->set_value (control->lower(), gcd);
698 /* Send mode: press enables/disables the relevant
699 * send, but the vpot is bound to the send-level so we
700 * need to lookup the enable/disable control
704 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
708 const uint32_t global_pos = _surface->mcp().global_index (*this);
709 boost::shared_ptr<AutomationControl> control = r->send_enable_controllable (global_pos);
712 bool currently_enabled = (bool) control->get_value();
713 Controllable::GroupControlDisposition gcd;
715 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
716 gcd = Controllable::InverseGroup;
718 gcd = Controllable::UseGroup;
721 control->set_value (!currently_enabled, gcd);
723 if (currently_enabled) {
724 /* we just turned it off */
725 pending_display[1] = "off";
727 /* we just turned it on, show the level
729 control = _stripable->send_level_controllable (global_pos);
730 do_parameter_display (control->desc(), control->get_value()); // BusSendLevel
736 /* done with this event in subview mode */
743 int ms = _surface->mcp().main_modifier_state();
745 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
747 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
751 /* reset to default/normal value */
752 ac->set_value (ac->normal(), Controllable::NoGroup);
759 boost::shared_ptr<AutomationControl> ac = _stripable->master_send_enable_controllable ();
761 Controllable::GroupControlDisposition gcd;
763 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
764 gcd = Controllable::InverseGroup;
766 gcd = Controllable::UseGroup;
769 bool enabled = ac->get_value();
770 ac->set_value (!enabled, gcd);
774 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
775 /* switch vpot to control next available parameter */
784 Strip::fader_touch_event (Button&, ButtonState bs)
786 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
790 boost::shared_ptr<AutomationControl> ac = _fader->control ();
792 _fader->set_in_use (true);
793 _fader->start_touch (_surface->mcp().transport_sample());
796 do_parameter_display (ac->desc(), ac->get_value());
801 _fader->set_in_use (false);
802 _fader->stop_touch (_surface->mcp().transport_sample());
809 Strip::handle_button (Button& button, ButtonState bs)
811 boost::shared_ptr<AutomationControl> control;
814 button.set_in_use (true);
816 button.set_in_use (false);
819 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
821 switch (button.bid()) {
823 select_event (button, bs);
826 case Button::VSelect:
827 vselect_event (button, bs);
830 case Button::FaderTouch:
831 fader_touch_event (button, bs);
835 if ((control = button.control ())) {
837 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
838 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
840 float new_value = control->get_value() ? 0.0 : 1.0;
842 /* get all controls that either have their
843 * button down or are within a range of
844 * several down buttons
847 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type(),
848 _surface->mcp().global_index(*this));
851 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
852 controls.size(), control->parameter().type(), new_value));
854 /* apply change, with potential modifier semantics */
856 Controllable::GroupControlDisposition gcd;
858 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
859 gcd = Controllable::InverseGroup;
861 gcd = Controllable::UseGroup;
864 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
865 (*c)->set_value (new_value, gcd);
869 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
870 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
878 Strip::do_parameter_display (ARDOUR::ParameterDescriptor const& desc, float val, bool screen_hold)
885 // we can't use value_as_string() that'll suffix "dB" and also use "-inf" w/o space :(
887 pending_display[1] = " -inf ";
889 float dB = accurate_coefficient_to_dB (val);
890 snprintf (buf, sizeof (buf), "%6.1f", dB);
891 pending_display[1] = buf;
897 if (Profile->get_mixbus()) { //Mixbus sends are already stored in dB
898 // TODO remove after merge - PluginAutomation w/print_fmt
899 snprintf (buf, sizeof (buf), "%2.1f", val);
900 pending_display[1] = buf;
904 pending_display[1] = " -inf ";
906 float dB = accurate_coefficient_to_dB (val);
907 snprintf (buf, sizeof (buf), "%6.1f", dB);
908 pending_display[1] = buf;
914 case PanAzimuthAutomation:
915 if (Profile->get_mixbus()) {
916 // XXX no _stripable check?
917 snprintf (buf, sizeof (buf), "%2.1f", val);
918 pending_display[1] = buf;
922 boost::shared_ptr<AutomationControl> pa = _stripable->pan_azimuth_control();
924 pending_display[1] = pa->get_user_string ();
931 pending_display[1] = ARDOUR::value_as_string (desc, val);
932 if (pending_display[1].size () < 6) { // left-padding, right-align
933 pending_display[1].insert (0, 6 - pending_display[1].size (), ' ');
939 /* we just queued up a parameter to be displayed.
940 1 second from now, switch back to vpot mode display.
942 block_vpot_mode_display_for (1000);
947 Strip::handle_fader_touch (Fader& fader, bool touch_on)
950 fader.start_touch (_surface->mcp().transport_sample());
952 fader.stop_touch (_surface->mcp().transport_sample());
957 Strip::handle_fader (Fader& fader, float position)
959 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
960 boost::shared_ptr<AutomationControl> ac = fader.control();
965 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
967 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
968 gcd = Controllable::InverseGroup;
971 fader.set_value (position, gcd);
973 /* From the Mackie Control MIDI implementation docs:
975 In order to ensure absolute synchronization with the host software,
976 Mackie Control uses a closed-loop servo system for the faders,
977 meaning the faders will always move to their last received position.
978 When a host receives a Fader Position Message, it must then
979 re-transmit that message to the Mackie Control or else the faders
980 will return to their last position.
983 _surface->write (fader.set_position (position));
987 Strip::handle_pot (Pot& pot, float delta)
989 /* Pots only emit events when they move, not when they
990 stop moving. So to get a stop event, we need to use a timeout.
993 boost::shared_ptr<AutomationControl> ac = pot.control();
998 Controllable::GroupControlDisposition gcd;
1000 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1001 gcd = Controllable::InverseGroup;
1003 gcd = Controllable::UseGroup;
1006 if (ac->toggled()) {
1008 /* make it like a single-step, directional switch */
1011 ac->set_value (1.0, gcd);
1013 ac->set_value (0.0, gcd);
1016 } else if (ac->desc().enumeration || ac->desc().integer_step) {
1018 /* use Controllable::get_value() to avoid the
1019 * "scaling-to-interface" that takes place in
1020 * Control::get_value() via the pot member.
1022 * an enumeration with 4 values will have interface values of
1023 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
1028 ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
1030 ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
1035 double p = ac->get_interface();
1042 ac->set_value ( ac->interface_to_internal(p), gcd);
1047 Strip::periodic (ARDOUR::microseconds_t now)
1050 update_automation ();
1054 Strip::redisplay (ARDOUR::microseconds_t now, bool force)
1056 if (_block_screen_redisplay_until >= now) {
1057 /* no drawing allowed */
1061 if (_block_screen_redisplay_until) {
1062 /* we were blocked, but the time period has elapsed, so we must
1066 _block_screen_redisplay_until = 0;
1069 if (force || (current_display[0] != pending_display[0])) {
1070 _surface->write (display (0, pending_display[0]));
1071 current_display[0] = pending_display[0];
1074 if (return_to_vpot_mode_display_at <= now) {
1075 return_to_vpot_mode_display_at = UINT64_MAX;
1076 return_to_vpot_mode_display ();
1079 if (force || (current_display[1] != pending_display[1])) {
1080 _surface->write (display (1, pending_display[1]));
1081 current_display[1] = pending_display[1];
1086 Strip::update_automation ()
1092 ARDOUR::AutoState state = _stripable->gain_control()->automation_state();
1094 if (state == Touch || state == Play) {
1095 notify_gain_changed (false);
1098 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
1100 state = pan_control->automation_state ();
1101 if (state == Touch || state == Play) {
1102 notify_panner_azi_changed (false);
1106 pan_control = _stripable->pan_width_control ();
1108 state = pan_control->automation_state ();
1109 if (state == Touch || state == Play) {
1110 notify_panner_width_changed (false);
1116 Strip::update_meter ()
1122 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1126 if (_meter && _transport_is_rolling && _metering_active && _stripable->peak_meter()) {
1127 float dB = _stripable->peak_meter()->meter_level (0, MeterMCP);
1128 _meter->send_update (*_surface, dB);
1136 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
1137 _surface->write ((*it)->zero ());
1140 _surface->write (blank_display (0));
1141 _surface->write (blank_display (1));
1142 pending_display[0] = string();
1143 pending_display[1] = string();
1144 current_display[0] = string();
1145 current_display[1] = string();
1149 Strip::blank_display (uint32_t line_number)
1151 return display (line_number, string());
1155 Strip::display (uint32_t line_number, const std::string& line)
1157 assert (line_number <= 1);
1159 MidiByteArray retval;
1161 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
1164 retval << _surface->sysex_hdr();
1168 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
1169 retval << (_index * 7 + (line_number * 0x38));
1171 // ascii data to display. @param line is UTF-8
1172 string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
1173 string::size_type len = ascii.length();
1175 ascii = ascii.substr (0, 6);
1179 // pad with " " out to 6 chars
1180 for (int i = len; i < 6; ++i) {
1184 // column spacer, unless it's the right-hand column
1190 retval << MIDI::eox;
1196 Strip::lock_controls ()
1198 _controls_locked = true;
1202 Strip::unlock_controls ()
1204 _controls_locked = false;
1208 Strip::vpot_mode_string ()
1210 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1214 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1220 switch (ac->desc().type) {
1221 case PanAzimuthAutomation:
1223 case PanWidthAutomation:
1225 case PanElevationAutomation:
1227 case PanFrontBackAutomation:
1229 case PanLFEAutomation:
1235 //"None" mode, by definition (currently) shows the pan control above the fader.
1236 //Mixbus controllers are created from a LADSPA so they don't have ac->desc().type
1237 //For the forseeable future, we will just return "Pan" here.
1245 Strip::flip_mode_changed ()
1247 if (_surface->mcp().subview_mode() == MackieControlProtocol::Sends) {
1249 boost::shared_ptr<AutomationControl> pot_control = _vpot->control();
1250 boost::shared_ptr<AutomationControl> fader_control = _fader->control();
1252 if (pot_control && fader_control) {
1254 _vpot->set_control (fader_control);
1255 _fader->set_control (pot_control);
1257 /* update fader with pot value */
1259 _surface->write (_fader->set_position (pot_control->internal_to_interface (pot_control->get_value ())));
1261 /* update pot with fader value */
1263 _surface->write (_vpot->set (fader_control->internal_to_interface (fader_control->get_value()), true, Pot::wrap));
1266 if (_surface->mcp().flip_mode() == MackieControlProtocol::Normal) {
1267 do_parameter_display (fader_control->desc(), fader_control->get_value());
1269 do_parameter_display (pot_control->desc(), pot_control->get_value()); // BusSendLevel
1280 Strip::block_screen_display_for (uint32_t msecs)
1282 _block_screen_redisplay_until = ARDOUR::get_microseconds() + (msecs * 1000);
1286 Strip::block_vpot_mode_display_for (uint32_t msecs)
1288 return_to_vpot_mode_display_at = ARDOUR::get_microseconds() + (msecs * 1000);
1292 Strip::return_to_vpot_mode_display ()
1294 /* returns the second line of the two-line per-strip display
1295 back the mode where it shows what the VPot controls.
1298 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1299 /* do nothing - second line shows value of current subview parameter */
1301 } else if (_stripable) {
1302 pending_display[1] = vpot_mode_string();
1304 pending_display[1] = string();
1309 Strip::next_pot_mode ()
1311 vector<AutomationType>::iterator i;
1313 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1314 /* do not change vpot mode while in flipped mode */
1315 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1316 pending_display[1] = "Flip";
1317 block_vpot_mode_display_for (1000);
1322 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1329 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1333 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1337 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1338 if ((*i) == ac->parameter().type()) {
1343 /* move to the next mode in the list, or back to the start (which will
1344 also happen if the current mode is not in the current pot mode list)
1347 if (i != possible_pot_parameters.end()) {
1351 if (i == possible_pot_parameters.end()) {
1352 i = possible_pot_parameters.begin();
1355 set_vpot_parameter (*i);
1359 Strip::subview_mode_changed ()
1361 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
1363 subview_connections.drop_connections ();
1365 switch (_surface->mcp().subview_mode()) {
1366 case MackieControlProtocol::None:
1367 set_vpot_parameter (_pan_mode);
1368 /* need to show strip name again */
1369 show_stripable_name ();
1371 _surface->write (_vpot->set (0, true, Pot::wrap));
1372 _surface->write (_fader->set_position (0.0));
1374 notify_metering_state_changed ();
1378 case MackieControlProtocol::EQ:
1382 /* leave it as it was */
1386 case MackieControlProtocol::Dynamics:
1390 /* leave it as it was */
1395 case MackieControlProtocol::Sends:
1397 setup_sends_vpot (r);
1399 /* leave it as it was */
1403 case MackieControlProtocol::TrackView:
1405 setup_trackview_vpot (r);
1407 /* leave it as it was */
1415 Strip::setup_dyn_vpot (boost::shared_ptr<Stripable> r)
1421 boost::shared_ptr<AutomationControl> tc = r->comp_threshold_controllable ();
1422 boost::shared_ptr<AutomationControl> sc = r->comp_speed_controllable ();
1423 boost::shared_ptr<AutomationControl> mc = r->comp_mode_controllable ();
1424 boost::shared_ptr<AutomationControl> kc = r->comp_makeup_controllable ();
1425 boost::shared_ptr<AutomationControl> ec = r->comp_enable_controllable ();
1427 #ifdef MIXBUS32C //Mixbus32C needs to spill the filter controls into the comp section
1428 boost::shared_ptr<AutomationControl> hpfc = r->filter_freq_controllable (true);
1429 boost::shared_ptr<AutomationControl> lpfc = r->filter_freq_controllable (false);
1430 boost::shared_ptr<AutomationControl> fec = r->filter_enable_controllable (true); // shared HP/LP
1433 uint32_t pos = _surface->mcp().global_index (*this);
1435 /* we will control the pos-th available parameter, from the list in the
1436 * order shown above.
1439 vector<std::pair<boost::shared_ptr<AutomationControl>, std::string > > available;
1440 vector<AutomationType> params;
1442 if (tc) { available.push_back (std::make_pair (tc, "Thresh")); }
1443 if (sc) { available.push_back (std::make_pair (sc, mc ? r->comp_speed_name (mc->get_value()) : "Speed")); }
1444 if (mc) { available.push_back (std::make_pair (mc, "Mode")); }
1445 if (kc) { available.push_back (std::make_pair (kc, "Makeup")); }
1446 if (ec) { available.push_back (std::make_pair (ec, "on/off")); }
1448 #ifdef MIXBUS32C //Mixbus32C needs to spill the filter controls into the comp section
1449 if (hpfc) { available.push_back (std::make_pair (hpfc, "HPF")); }
1450 if (lpfc) { available.push_back (std::make_pair (lpfc, "LPF")); }
1451 if (fec) { available.push_back (std::make_pair (fec, "FiltIn")); }
1454 if (pos >= available.size()) {
1455 /* this knob is not needed to control the available parameters */
1456 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1457 pending_display[0] = string();
1458 pending_display[1] = string();
1462 boost::shared_ptr<AutomationControl> pc;
1464 pc = available[pos].first;
1465 string pot_id = available[pos].second;
1467 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_dyn_change, this, boost::weak_ptr<AutomationControl>(pc), false, true), ui_context());
1468 _vpot->set_control (pc);
1470 if (!pot_id.empty()) {
1471 pending_display[0] = pot_id;
1473 pending_display[0] = string();
1476 notify_dyn_change (boost::weak_ptr<AutomationControl>(pc), true, false);
1480 Strip::setup_eq_vpot (boost::shared_ptr<Stripable> r)
1482 boost::shared_ptr<AutomationControl> pc;
1486 const uint32_t global_pos = _surface->mcp().global_index (*this);
1488 std::string band_name;
1489 if (r->is_input_strip ()) {
1492 switch (global_pos) {
1497 eq_band = global_pos / 2;
1498 pc = r->eq_freq_controllable (eq_band);
1499 band_name = r->eq_band_name (eq_band);
1500 pot_id = band_name + "Freq";
1506 eq_band = global_pos / 2;
1507 pc = r->eq_gain_controllable (eq_band);
1508 band_name = r->eq_band_name (eq_band);
1509 pot_id = band_name + "Gain";
1512 pc = r->eq_shape_controllable(0); //low band "bell" button
1514 pot_id = band_name + " Shp";
1517 pc = r->eq_shape_controllable(3); //high band "bell" button
1519 pot_id = band_name + " Shp";
1522 pc = r->eq_enable_controllable();
1527 #else //regular Mixbus channel EQ
1529 switch (global_pos) {
1533 eq_band = global_pos / 2;
1534 pc = r->eq_gain_controllable (eq_band);
1535 band_name = r->eq_band_name (eq_band);
1536 pot_id = band_name + "Gain";
1541 eq_band = global_pos / 2;
1542 pc = r->eq_freq_controllable (eq_band);
1543 band_name = r->eq_band_name (eq_band);
1544 pot_id = band_name + "Freq";
1547 pc = r->eq_enable_controllable();
1551 pc = r->filter_freq_controllable(true);
1558 } else { //mixbus or master bus ( these are currently the same for MB & 32C )
1559 switch (global_pos) {
1563 eq_band = global_pos;
1564 pc = r->eq_gain_controllable (eq_band);
1565 band_name = r->eq_band_name (eq_band);
1566 pot_id = band_name + "Gain";
1572 //If a controllable was found, connect it up, and put the labels in the display.
1574 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_eq_change, this, boost::weak_ptr<AutomationControl>(pc), false), ui_context());
1575 _vpot->set_control (pc);
1577 if (!pot_id.empty()) {
1578 pending_display[0] = pot_id;
1580 pending_display[0] = string();
1583 } else { //no controllable was found; just clear this knob
1584 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1585 pending_display[0] = string();
1586 pending_display[1] = string();
1589 notify_eq_change (boost::weak_ptr<AutomationControl>(pc), true);
1593 Strip::setup_sends_vpot (boost::shared_ptr<Stripable> r)
1599 const uint32_t global_pos = _surface->mcp().global_index (*this);
1601 boost::shared_ptr<AutomationControl> pc = r->send_level_controllable (global_pos);
1604 /* nothing to control */
1605 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1606 pending_display[0] = string();
1607 pending_display[1] = string();
1611 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_send_level_change, this, global_pos, false), ui_context());
1612 _vpot->set_control (pc);
1614 pending_display[0] = PBD::short_version (r->send_name (global_pos), 6);
1616 notify_send_level_change (global_pos, true);
1620 Strip::setup_trackview_vpot (boost::shared_ptr<Stripable> r)
1626 const uint32_t global_pos = _surface->mcp().global_index (*this);
1628 if (global_pos >= 8) {
1629 /* nothing to control */
1630 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1631 pending_display[0] = string();
1632 pending_display[1] = string();
1636 boost::shared_ptr<AutomationControl> pc;
1637 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
1640 switch (global_pos) {
1642 pc = r->trim_control ();
1644 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, TrimAutomation, global_pos, false), ui_context());
1645 pending_display[0] = "Trim";
1646 notify_trackview_change (TrimAutomation, global_pos, true);
1651 pc = track->monitoring_control();
1653 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, MonitoringAutomation, global_pos, false), ui_context());
1654 pending_display[0] = "Mon";
1655 notify_trackview_change (MonitoringAutomation, global_pos, true);
1660 pc = r->solo_isolate_control ();
1662 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloIsolateAutomation, global_pos, false), ui_context());
1663 notify_trackview_change (SoloIsolateAutomation, global_pos, true);
1664 pending_display[0] = "S-Iso";
1668 pc = r->solo_safe_control ();
1670 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloSafeAutomation, global_pos, false), ui_context());
1671 notify_trackview_change (SoloSafeAutomation, global_pos, true);
1672 pending_display[0] = "S-Safe";
1676 pc = r->phase_control();
1678 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, PhaseAutomation, global_pos, false), ui_context());
1679 notify_trackview_change (PhaseAutomation, global_pos, true);
1680 pending_display[0] = "Phase";
1684 // pc = r->trim_control ();
1687 // pc = r->trim_control ();
1690 // pc = r->trim_control ();
1695 pending_display[0] = string();
1696 pending_display[1] = string();
1700 _vpot->set_control (pc);
1704 Strip::set_vpot_parameter (AutomationType p)
1706 if (!_stripable || (p == NullAutomation)) {
1707 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1708 pending_display[1] = string();
1712 boost::shared_ptr<AutomationControl> pan_control;
1714 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1716 reset_saved_values ();
1719 case PanAzimuthAutomation:
1720 pan_control = _stripable->pan_azimuth_control ();
1722 case PanWidthAutomation:
1723 pan_control = _stripable->pan_width_control ();
1725 case PanElevationAutomation:
1727 case PanFrontBackAutomation:
1729 case PanLFEAutomation:
1737 _vpot->set_control (pan_control);
1740 pending_display[1] = vpot_mode_string ();
1744 Strip::is_midi_track () const
1746 return boost::dynamic_pointer_cast<MidiTrack>(_stripable) != 0;
1750 Strip::reset_saved_values ()
1752 _last_pan_azi_position_written = -1.0;
1753 _last_pan_width_position_written = -1.0;
1754 _last_gain_position_written = -1.0;
1755 _last_trim_position_written = -1.0;
1760 Strip::notify_metering_state_changed()
1762 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1766 if (!_stripable || !_meter) {
1770 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1771 bool metering_active = _surface->mcp().metering_active ();
1773 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1777 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1779 if (!transport_is_rolling || !metering_active) {
1780 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1781 notify_panner_azi_changed (true);
1784 _transport_is_rolling = transport_is_rolling;
1785 _metering_active = metering_active;