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/route.h"
48 #include "ardour/session.h"
49 #include "ardour/send.h"
50 #include "ardour/solo_isolate_control.h"
51 #include "ardour/track.h"
52 #include "ardour/midi_track.h"
53 #include "ardour/user_bundle.h"
54 #include "ardour/profile.h"
56 #include "mackie_control_protocol.h"
57 #include "surface_port.h"
68 using namespace ARDOUR;
70 using namespace ArdourSurface;
71 using namespace Mackie;
73 #ifndef timeradd /// only avail with __USE_BSD
74 #define timeradd(a,b,result) \
76 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
77 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
78 if ((result)->tv_usec >= 1000000) \
81 (result)->tv_usec -= 1000000; \
86 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
88 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
101 , _controls_locked (false)
102 , _transport_is_rolling (false)
103 , _metering_active (true)
104 , _block_screen_redisplay_until (0)
105 , return_to_vpot_mode_display_at (UINT64_MAX)
107 , _pan_mode (PanAzimuthAutomation)
108 , _last_gain_position_written (-1.0)
109 , _last_pan_azi_position_written (-1.0)
110 , _last_pan_width_position_written (-1.0)
111 , _last_trim_position_written (-1.0)
113 _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
114 _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
116 if (s.mcp().device_info().has_meters()) {
117 _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
120 for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
121 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
122 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
123 _surface->number(), index, Button::id_to_name (bb->bid()),
124 bb->id(), b->second.base_id));
130 /* surface is responsible for deleting all controls */
134 Strip::add (Control & control)
138 Group::add (control);
140 /* fader, vpot, meter were all set explicitly */
142 if ((button = dynamic_cast<Button*>(&control)) != 0) {
143 switch (button->bid()) {
144 case Button::RecEnable:
156 case Button::VSelect:
159 case Button::FaderTouch:
160 _fader_touch = button;
169 Strip::set_route (boost::shared_ptr<Route> r, bool /*with_messages*/)
171 if (_controls_locked) {
175 mb_pan_controllable.reset();
177 route_connections.drop_connections ();
179 _solo->set_control (boost::shared_ptr<AutomationControl>());
180 _mute->set_control (boost::shared_ptr<AutomationControl>());
181 _select->set_control (boost::shared_ptr<AutomationControl>());
182 _recenable->set_control (boost::shared_ptr<AutomationControl>());
183 _fader->set_control (boost::shared_ptr<AutomationControl>());
184 _vpot->set_control (boost::shared_ptr<AutomationControl>());
188 reset_saved_values ();
191 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 Strip %2 mapped to null route\n", _surface->number(), _index));
196 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping route %3\n",
197 _surface->number(), _index, _route->name()));
199 _solo->set_control (_route->solo_control());
200 _mute->set_control (_route->mute_control());
202 _route->solo_control()->Changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
203 _route->mute_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
205 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control();
207 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
210 pan_control = _route->pan_width_control();
212 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
215 _route->gain_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
216 _route->PropertyChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
218 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
221 _recenable->set_control (trk->rec_enable_control());
222 trk->rec_enable_control()->Changed .connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
225 // TODO this works when a currently-banked route is made inactive, but not
226 // when a route is activated which should be currently banked.
228 _route->active_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_active_changed, this), ui_context());
229 _route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_route_deleted, this), ui_context());
231 /* setup legal VPot modes for this route */
233 possible_pot_parameters.clear();
235 if (_route->pan_azimuth_control()) {
236 possible_pot_parameters.push_back (PanAzimuthAutomation);
238 if (_route->pan_width_control()) {
239 possible_pot_parameters.push_back (PanWidthAutomation);
241 if (_route->pan_elevation_control()) {
242 possible_pot_parameters.push_back (PanElevationAutomation);
244 if (_route->pan_frontback_control()) {
245 possible_pot_parameters.push_back (PanFrontBackAutomation);
247 if (_route->pan_lfe_control()) {
248 possible_pot_parameters.push_back (PanLFEAutomation);
251 _pan_mode = PanAzimuthAutomation;
253 if (_surface->mcp().subview_mode() == MackieControlProtocol::None) {
254 set_vpot_parameter (_pan_mode);
257 _fader->set_control (_route->gain_control());
269 // The active V-pot control may not be active for this strip
270 // But if we zero it in the controls function it may erase
271 // the one we do want
272 _surface->write (_vpot->zero());
274 notify_solo_changed ();
275 notify_mute_changed ();
276 notify_gain_changed ();
277 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
278 notify_panner_azi_changed ();
279 notify_panner_width_changed ();
280 notify_record_enable_changed ();
281 notify_processor_changed ();
285 Strip::notify_solo_changed ()
287 if (_route && _solo) {
288 _surface->write (_solo->set_state (_route->soloed() ? on : off));
293 Strip::notify_mute_changed ()
295 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
296 if (_route && _mute) {
297 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\troute muted ? %1\n", _route->muted()));
298 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_route->muted() ? on : off)));
300 _surface->write (_mute->set_state (_route->muted() ? on : off));
305 Strip::notify_record_enable_changed ()
307 if (_route && _recenable) {
308 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (_route);
310 _surface->write (_recenable->set_state (trk->rec_enable_control()->get_value() ? on : off));
316 Strip::notify_active_changed ()
318 _surface->mcp().refresh_current_bank();
322 Strip::notify_route_deleted ()
324 _surface->mcp().notify_route_removed ();
325 _surface->mcp().refresh_current_bank();
329 Strip::notify_gain_changed (bool force_update)
335 boost::shared_ptr<AutomationControl> ac = _route->gain_control();
339 /* doesn't seem possible but lets be safe */
343 /* track gain control could be on vpot or fader, depending in
347 if (_vpot->control() == ac) {
349 } else if (_fader->control() == ac) {
355 float gain_coefficient = ac->get_value();
356 float normalized_position = ac->internal_to_interface (gain_coefficient);
358 if (force_update || normalized_position != _last_gain_position_written) {
360 if (!control->in_use()) {
361 if (control == _vpot) {
362 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
364 _surface->write (_fader->set_position (normalized_position));
368 do_parameter_display (GainAutomation, gain_coefficient);
369 _last_gain_position_written = normalized_position;
374 Strip::notify_processor_changed (bool force_update)
379 Strip::notify_property_changed (const PropertyChange& what_changed)
381 if (!what_changed.contains (ARDOUR::Properties::name)) {
389 Strip::show_route_name ()
391 MackieControlProtocol::SubViewMode svm = _surface->mcp().subview_mode();
393 if (svm != MackieControlProtocol::None) {
394 /* subview mode is responsible for upper line */
398 string fullname = string();
402 fullname = _route->name();
405 if (fullname.length() <= 6) {
406 pending_display[0] = fullname;
408 pending_display[0] = PBD::short_version (fullname, 6);
413 Strip::notify_send_level_change (AutomationType type, uint32_t send_num, bool force_update)
415 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
418 /* not in subview mode */
422 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
423 /* no longer in Sends subview mode */
427 boost::shared_ptr<AutomationControl> control = r->send_level_controllable (send_num);
433 float val = control->get_value();
434 do_parameter_display (type, val);
436 if (_vpot->control() == control) {
437 /* update pot/encoder */
438 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
444 Strip::notify_trackview_change (AutomationType type, uint32_t send_num, bool force_update)
446 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
449 /* not in subview mode */
453 if (_surface->mcp().subview_mode() != MackieControlProtocol::TrackView) {
454 /* no longer in TrackViewsubview mode */
458 boost::shared_ptr<AutomationControl> control;
459 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
463 control = r->trim_control();
465 case SoloIsolateAutomation:
466 control = r->solo_isolate_control ();
468 case SoloSafeAutomation:
469 control = r->solo_safe_control ();
471 case MonitoringAutomation:
473 control = track->monitoring_control();
476 case PhaseAutomation:
477 control = r->phase_control ();
484 float val = control->get_value();
486 /* Note: all of the displayed controllables require the display
487 * of their *actual* ("internal") value, not the version mapped
488 * into the normalized 0..1.0 ("interface") range.
491 do_parameter_display (type, val);
492 /* update pot/encoder */
493 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
498 Strip::notify_eq_change (AutomationType type, uint32_t band, bool force_update)
500 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
503 /* not in subview mode */
507 if (_surface->mcp().subview_mode() != MackieControlProtocol::EQ) {
508 /* no longer in EQ subview mode */
512 boost::shared_ptr<AutomationControl> control;
516 control = r->eq_gain_controllable (band);
519 control = r->eq_freq_controllable (band);
522 control = r->eq_q_controllable (band);
525 control = r->eq_shape_controllable (band);
528 control = r->eq_hpf_controllable ();
531 control = r->eq_enable_controllable ();
538 float val = control->get_value();
539 do_parameter_display (type, val);
540 /* update pot/encoder */
541 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
546 Strip::notify_dyn_change (AutomationType type, bool force_update, bool propagate_mode)
548 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
551 /* not in subview mode */
555 if (_surface->mcp().subview_mode() != MackieControlProtocol::Dynamics) {
556 /* no longer in EQ subview mode */
560 boost::shared_ptr<AutomationControl> control;
561 bool reset_all = false;
565 control = r->comp_threshold_controllable ();
568 control = r->comp_speed_controllable ();
571 control = r->comp_mode_controllable ();
575 control = r->comp_makeup_controllable ();
578 control = r->comp_redux_controllable ();
581 control = r->comp_enable_controllable ();
587 if (propagate_mode && reset_all) {
588 _surface->subview_mode_changed ();
592 float val = control->get_value();
593 do_parameter_display (type, val);
594 /* update pot/encoder */
595 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
600 Strip::notify_panner_azi_changed (bool force_update)
606 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
608 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
611 /* basically impossible, since we're here because that control
612 * changed, but sure, whatever.
617 if (_vpot->control() != pan_control) {
621 double normalized_pos = pan_control->internal_to_interface (pan_control->get_value());
622 double internal_pos = pan_control->get_value();
624 if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
626 _surface->write (_vpot->set (normalized_pos, true, Pot::dot));
627 /* show actual internal value to user */
628 do_parameter_display (PanAzimuthAutomation, internal_pos);
630 _last_pan_azi_position_written = normalized_pos;
635 Strip::notify_panner_width_changed (bool force_update)
641 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
643 boost::shared_ptr<AutomationControl> pan_control = _route->pan_width_control ();
646 /* basically impossible, since we're here because that control
647 * changed, but sure, whatever.
652 if (_vpot->control() != pan_control) {
656 double pos = pan_control->internal_to_interface (pan_control->get_value());
658 if (force_update || pos != _last_pan_width_position_written) {
660 _surface->write (_vpot->set (pos, true, Pot::spread));
661 do_parameter_display (PanWidthAutomation, pos);
663 _last_pan_width_position_written = pos;
668 Strip::select_event (Button&, ButtonState bs)
670 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
674 int ms = _surface->mcp().main_modifier_state();
676 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
677 _controls_locked = !_controls_locked;
678 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
679 block_vpot_mode_display_for (1000);
683 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
684 _surface->mcp().add_down_select_button (_surface->number(), _index);
685 _surface->mcp().select_range ();
688 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
689 _surface->mcp().remove_down_select_button (_surface->number(), _index);
694 Strip::vselect_event (Button&, ButtonState bs)
696 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
698 /* most subview modes: vpot press acts like a button for toggle parameters */
704 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
706 boost::shared_ptr<AutomationControl> control = _vpot->control ();
711 Controllable::GroupControlDisposition gcd;
712 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
713 gcd = Controllable::InverseGroup;
715 gcd = Controllable::UseGroup;
718 if (control->toggled()) {
719 if (control->toggled()) {
720 control->set_value (!control->get_value(), gcd);
723 } else if (control->desc().enumeration || control->desc().integer_step) {
725 double val = control->get_value ();
726 if (val <= control->upper() - 1.0) {
727 control->set_value (val + 1.0, gcd);
729 control->set_value (control->lower(), gcd);
735 /* Send mode: press enables/disables the relevant
736 * send, but the vpot is bound to the send-level so we
737 * need to lookup the enable/disable control
741 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
745 const uint32_t global_pos = _surface->mcp().global_index (*this);
746 boost::shared_ptr<AutomationControl> control = r->send_enable_controllable (global_pos);
749 bool currently_enabled = (bool) control->get_value();
750 Controllable::GroupControlDisposition gcd;
752 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
753 gcd = Controllable::InverseGroup;
755 gcd = Controllable::UseGroup;
758 control->set_value (!currently_enabled, gcd);
760 if (currently_enabled) {
761 /* we just turned it off */
762 pending_display[1] = "off";
764 /* we just turned it on, show the level
766 control = _route->send_level_controllable (global_pos);
767 do_parameter_display (BusSendLevel, control->get_value());
773 /* done with this event in subview mode */
780 int ms = _surface->mcp().main_modifier_state();
782 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
784 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
788 /* reset to default/normal value */
789 ac->set_value (ac->normal(), Controllable::NoGroup);
796 boost::shared_ptr<AutomationControl> ac = _route->master_send_enable_controllable ();
798 Controllable::GroupControlDisposition gcd;
800 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
801 gcd = Controllable::InverseGroup;
803 gcd = Controllable::UseGroup;
806 bool enabled = ac->get_value();
807 ac->set_value (!enabled, gcd);
811 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
812 /* switch vpot to control next available parameter */
821 Strip::fader_touch_event (Button&, ButtonState bs)
823 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
827 boost::shared_ptr<AutomationControl> ac = _fader->control ();
829 _fader->set_in_use (true);
830 _fader->start_touch (_surface->mcp().transport_frame());
833 do_parameter_display ((AutomationType) ac->parameter().type(), ac->get_value());
838 _fader->set_in_use (false);
839 _fader->stop_touch (_surface->mcp().transport_frame(), true);
846 Strip::handle_button (Button& button, ButtonState bs)
848 boost::shared_ptr<AutomationControl> control;
851 button.set_in_use (true);
853 button.set_in_use (false);
856 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
858 switch (button.bid()) {
860 select_event (button, bs);
863 case Button::VSelect:
864 vselect_event (button, bs);
867 case Button::FaderTouch:
868 fader_touch_event (button, bs);
872 if ((control = button.control ())) {
874 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
875 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
877 float new_value = control->get_value() ? 0.0 : 1.0;
879 /* get all controls that either have their
880 * button down or are within a range of
881 * several down buttons
884 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type());
887 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
888 controls.size(), control->parameter().type(), new_value));
890 /* apply change, with potential modifier semantics */
892 Controllable::GroupControlDisposition gcd;
894 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
895 gcd = Controllable::InverseGroup;
897 gcd = Controllable::UseGroup;
900 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
901 (*c)->set_value (new_value, gcd);
905 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
906 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
914 Strip::do_parameter_display (AutomationType type, float val)
916 bool screen_hold = false;
922 pending_display[1] = " -inf ";
924 float dB = accurate_coefficient_to_dB (val);
925 snprintf (buf, sizeof (buf), "%6.1f", dB);
926 pending_display[1] = buf;
932 if (Profile->get_mixbus()) { //Mixbus sends are already stored in dB
933 snprintf (buf, sizeof (buf), "%2.1f", val);
934 pending_display[1] = buf;
938 pending_display[1] = " -inf ";
940 float dB = accurate_coefficient_to_dB (val);
941 snprintf (buf, sizeof (buf), "%6.1f", dB);
942 pending_display[1] = buf;
948 case PanAzimuthAutomation:
949 if (Profile->get_mixbus()) {
950 snprintf (buf, sizeof (buf), "%2.1f", val);
951 pending_display[1] = buf;
955 boost::shared_ptr<Pannable> p = _route->pannable();
956 if (p && _route->panner()) {
957 pending_display[1] =_route->panner()->value_as_string (p->pan_azimuth_control);
964 case PanWidthAutomation:
966 snprintf (buf, sizeof (buf), "%5ld%%", lrintf ((val * 200.0)-100));
967 pending_display[1] = buf;
974 float dB = accurate_coefficient_to_dB (val);
975 snprintf (buf, sizeof (buf), "%6.1f", dB);
976 pending_display[1] = buf;
981 case PhaseAutomation:
984 pending_display[1] = "Normal";
986 pending_display[1] = "Invert";
1001 snprintf (buf, sizeof (buf), "%6.1f", val);
1002 pending_display[1] = buf;
1008 pending_display[1] = "on";
1010 pending_display[1] = "off";
1014 if (_surface->mcp().subview_route()) {
1015 pending_display[1] = _surface->mcp().subview_route()->comp_mode_name (val);
1018 case SoloSafeAutomation:
1019 case SoloIsolateAutomation:
1021 pending_display[1] = "on";
1023 pending_display[1] = "off";
1026 case MonitoringAutomation:
1027 switch (MonitorChoice ((int) val)) {
1029 pending_display[1] = "auto";
1032 pending_display[1] = "input";
1035 pending_display[1] = "disk";
1037 case MonitorCue: /* XXX not implemented as of jan 2016 */
1038 pending_display[1] = "cue";
1047 /* we just queued up a parameter to be displayed.
1048 1 second from now, switch back to vpot mode display.
1050 block_vpot_mode_display_for (1000);
1055 Strip::handle_fader_touch (Fader& fader, bool touch_on)
1058 fader.start_touch (_surface->mcp().transport_frame());
1060 fader.stop_touch (_surface->mcp().transport_frame(), false);
1065 Strip::handle_fader (Fader& fader, float position)
1067 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
1068 boost::shared_ptr<AutomationControl> ac = fader.control();
1073 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
1075 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1076 gcd = Controllable::InverseGroup;
1079 fader.set_value (position, gcd);
1081 /* From the Mackie Control MIDI implementation docs:
1083 In order to ensure absolute synchronization with the host software,
1084 Mackie Control uses a closed-loop servo system for the faders,
1085 meaning the faders will always move to their last received position.
1086 When a host receives a Fader Position Message, it must then
1087 re-transmit that message to the Mackie Control or else the faders
1088 will return to their last position.
1091 _surface->write (fader.set_position (position));
1095 Strip::handle_pot (Pot& pot, float delta)
1097 /* Pots only emit events when they move, not when they
1098 stop moving. So to get a stop event, we need to use a timeout.
1101 boost::shared_ptr<AutomationControl> ac = pot.control();
1106 Controllable::GroupControlDisposition gcd;
1108 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1109 gcd = Controllable::InverseGroup;
1111 gcd = Controllable::UseGroup;
1114 if (ac->toggled()) {
1116 /* make it like a single-step, directional switch */
1119 ac->set_value (1.0, gcd);
1121 ac->set_value (0.0, gcd);
1124 } else if (ac->desc().enumeration || ac->desc().integer_step) {
1126 /* use Controllable::get_value() to avoid the
1127 * "scaling-to-interface" that takes place in
1128 * Control::get_value() via the pot member.
1130 * an enumeration with 4 values will have interface values of
1131 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
1136 ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
1138 ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
1143 double p = ac->get_interface();
1150 ac->set_value ( ac->interface_to_internal(p), gcd);
1155 Strip::periodic (ARDOUR::microseconds_t now)
1158 update_automation ();
1162 Strip::redisplay (ARDOUR::microseconds_t now, bool force)
1164 if (_block_screen_redisplay_until >= now) {
1165 /* no drawing allowed */
1169 if (_block_screen_redisplay_until) {
1170 /* we were blocked, but the time period has elapsed, so we must
1174 _block_screen_redisplay_until = 0;
1177 if (force || (current_display[0] != pending_display[0])) {
1178 _surface->write (display (0, pending_display[0]));
1179 current_display[0] = pending_display[0];
1182 if (return_to_vpot_mode_display_at <= now) {
1183 return_to_vpot_mode_display_at = UINT64_MAX;
1184 return_to_vpot_mode_display ();
1187 if (force || (current_display[1] != pending_display[1])) {
1188 _surface->write (display (1, pending_display[1]));
1189 current_display[1] = pending_display[1];
1194 Strip::update_automation ()
1200 ARDOUR::AutoState state = _route->gain_control()->automation_state();
1202 if (state == Touch || state == Play) {
1203 notify_gain_changed (false);
1206 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
1208 state = pan_control->automation_state ();
1209 if (state == Touch || state == Play) {
1210 notify_panner_azi_changed (false);
1214 pan_control = _route->pan_width_control ();
1216 state = pan_control->automation_state ();
1217 if (state == Touch || state == Play) {
1218 notify_panner_width_changed (false);
1224 Strip::update_meter ()
1230 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1234 if (_meter && _transport_is_rolling && _metering_active) {
1235 float dB = _route->peak_meter()->meter_level (0, MeterMCP);
1236 _meter->send_update (*_surface, dB);
1244 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
1245 _surface->write ((*it)->zero ());
1248 _surface->write (blank_display (0));
1249 _surface->write (blank_display (1));
1250 pending_display[0] = string();
1251 pending_display[1] = string();
1252 current_display[0] = string();
1253 current_display[1] = string();
1257 Strip::blank_display (uint32_t line_number)
1259 return display (line_number, string());
1263 Strip::display (uint32_t line_number, const std::string& line)
1265 assert (line_number <= 1);
1267 MidiByteArray retval;
1269 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
1272 retval << _surface->sysex_hdr();
1276 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
1277 retval << (_index * 7 + (line_number * 0x38));
1279 // ascii data to display. @param line is UTF-8
1280 string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
1281 string::size_type len = ascii.length();
1283 ascii = ascii.substr (0, 6);
1287 // pad with " " out to 6 chars
1288 for (int i = len; i < 6; ++i) {
1292 // column spacer, unless it's the right-hand column
1298 retval << MIDI::eox;
1304 Strip::lock_controls ()
1306 _controls_locked = true;
1310 Strip::unlock_controls ()
1312 _controls_locked = false;
1316 Strip::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& rl)
1318 for (ARDOUR::StrongRouteNotificationList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
1319 if ((*i) == _route) {
1320 _surface->write (_select->set_state (on));
1325 _surface->write (_select->set_state (off));
1329 Strip::vpot_mode_string ()
1331 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1335 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1341 switch (ac->desc().type) {
1342 case PanAzimuthAutomation:
1344 case PanWidthAutomation:
1346 case PanElevationAutomation:
1348 case PanFrontBackAutomation:
1350 case PanLFEAutomation:
1360 Strip::flip_mode_changed ()
1362 if (_surface->mcp().subview_mode() == MackieControlProtocol::Sends) {
1364 boost::shared_ptr<AutomationControl> pot_control = _vpot->control();
1365 boost::shared_ptr<AutomationControl> fader_control = _fader->control();
1367 if (pot_control && fader_control) {
1368 _vpot->set_control (fader_control);
1369 _fader->set_control (pot_control);
1372 if (_surface->mcp().flip_mode() == MackieControlProtocol::Normal) {
1373 do_parameter_display (GainAutomation, fader_control->get_value());
1375 do_parameter_display (BusSendLevel, fader_control->get_value());
1380 _surface->write (_fader->set_position (pot_control->internal_to_interface (pot_control->get_value ())));
1384 _surface->write (_vpot->set (fader_control->internal_to_interface (fader_control->get_value()), true, Pot::wrap));
1393 Strip::block_screen_display_for (uint32_t msecs)
1395 _block_screen_redisplay_until = ARDOUR::get_microseconds() + (msecs * 1000);
1399 Strip::block_vpot_mode_display_for (uint32_t msecs)
1401 return_to_vpot_mode_display_at = ARDOUR::get_microseconds() + (msecs * 1000);
1405 Strip::return_to_vpot_mode_display ()
1407 /* returns the second line of the two-line per-strip display
1408 back the mode where it shows what the VPot controls.
1411 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1412 /* do nothing - second line shows value of current subview parameter */
1414 } else if (_route) {
1415 pending_display[1] = vpot_mode_string();
1417 pending_display[1] = string();
1422 Strip::next_pot_mode ()
1424 vector<AutomationType>::iterator i;
1426 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1427 /* do not change vpot mode while in flipped mode */
1428 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1429 pending_display[1] = "Flip";
1430 block_vpot_mode_display_for (1000);
1435 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1442 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1446 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1450 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1451 if ((*i) == ac->parameter().type()) {
1456 /* move to the next mode in the list, or back to the start (which will
1457 also happen if the current mode is not in the current pot mode list)
1460 if (i != possible_pot_parameters.end()) {
1464 if (i == possible_pot_parameters.end()) {
1465 i = possible_pot_parameters.begin();
1468 set_vpot_parameter (*i);
1472 Strip::subview_mode_changed ()
1474 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
1476 subview_connections.drop_connections ();
1478 switch (_surface->mcp().subview_mode()) {
1479 case MackieControlProtocol::None:
1480 set_vpot_parameter (_pan_mode);
1481 /* need to show strip name again */
1484 _surface->write (_vpot->set (0, true, Pot::wrap));
1485 _surface->write (_fader->set_position (0.0));
1487 notify_metering_state_changed ();
1491 case MackieControlProtocol::EQ:
1495 /* leave it as it was */
1499 case MackieControlProtocol::Dynamics:
1503 /* leave it as it was */
1508 case MackieControlProtocol::Sends:
1510 setup_sends_vpot (r);
1512 /* leave it as it was */
1516 case MackieControlProtocol::TrackView:
1518 setup_trackview_vpot (r);
1520 /* leave it as it was */
1528 Strip::setup_dyn_vpot (boost::shared_ptr<Route> r)
1534 boost::shared_ptr<AutomationControl> tc = r->comp_threshold_controllable ();
1535 boost::shared_ptr<AutomationControl> sc = r->comp_speed_controllable ();
1536 boost::shared_ptr<AutomationControl> mc = r->comp_mode_controllable ();
1537 boost::shared_ptr<AutomationControl> kc = r->comp_makeup_controllable ();
1538 boost::shared_ptr<AutomationControl> rc = r->comp_redux_controllable ();
1539 boost::shared_ptr<AutomationControl> ec = r->comp_enable_controllable ();
1541 uint32_t pos = _surface->mcp().global_index (*this);
1543 /* we will control the pos-th available parameter, from the list in the
1544 * order shown above.
1547 vector<boost::shared_ptr<AutomationControl> > available;
1548 vector<AutomationType> params;
1550 if (tc) { available.push_back (tc); params.push_back (CompThreshold); }
1551 if (sc) { available.push_back (sc); params.push_back (CompSpeed); }
1552 if (mc) { available.push_back (mc); params.push_back (CompMode); }
1553 if (kc) { available.push_back (kc); params.push_back (CompMakeup); }
1554 if (rc) { available.push_back (rc); params.push_back (CompRedux); }
1555 if (ec) { available.push_back (ec); params.push_back (CompEnable); }
1557 if (pos >= available.size()) {
1558 /* this knob is not needed to control the available parameters */
1559 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1560 pending_display[0] = string();
1561 pending_display[1] = string();
1565 boost::shared_ptr<AutomationControl> pc;
1566 AutomationType param;
1568 pc = available[pos];
1569 param = params[pos];
1571 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_dyn_change, this, param, false, true), ui_context());
1572 _vpot->set_control (pc);
1582 pot_id = r->comp_speed_name (mc->get_value());
1603 if (!pot_id.empty()) {
1604 pending_display[0] = pot_id;
1606 pending_display[0] = string();
1609 notify_dyn_change (param, true, false);
1613 Strip::setup_eq_vpot (boost::shared_ptr<Route> r)
1615 uint32_t bands = r->eq_band_cnt ();
1618 /* should never get here */
1622 /* figure out how many params per band are available */
1624 boost::shared_ptr<AutomationControl> pc;
1625 uint32_t params_per_band = 0;
1627 if ((pc = r->eq_gain_controllable (0))) {
1628 params_per_band += 1;
1630 if ((pc = r->eq_freq_controllable (0))) {
1631 params_per_band += 1;
1633 if ((pc = r->eq_q_controllable (0))) {
1634 params_per_band += 1;
1636 if ((pc = r->eq_shape_controllable (0))) {
1637 params_per_band += 1;
1640 /* pick the one for this strip, based on its global position across
1646 const uint32_t total_band_parameters = bands * params_per_band;
1647 const uint32_t global_pos = _surface->mcp().global_index (*this);
1648 AutomationType param = NullAutomation;
1653 if (global_pos < total_band_parameters) {
1655 /* show a parameter for an EQ band */
1657 const uint32_t parameter = global_pos % params_per_band;
1658 eq_band = global_pos / params_per_band;
1659 band_name = r->eq_band_name (eq_band);
1661 switch (parameter) {
1663 pc = r->eq_gain_controllable (eq_band);
1667 pc = r->eq_freq_controllable (eq_band);
1668 param = EQFrequency;
1671 pc = r->eq_q_controllable (eq_band);
1675 pc = r->eq_shape_controllable (eq_band);
1682 /* show a non-band parameter (HPF or enable)
1685 uint32_t parameter = global_pos - total_band_parameters;
1687 switch (parameter) {
1688 case 0: /* first control after band parameters */
1689 pc = r->eq_hpf_controllable();
1692 case 1: /* second control after band parameters */
1693 pc = r->eq_enable_controllable();
1697 /* nothing to control */
1698 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1699 pending_display[0] = string();
1700 pending_display[1] = string();
1709 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_eq_change, this, param, eq_band, false), ui_context());
1710 _vpot->set_control (pc);
1716 pot_id = band_name + "Gain";
1719 pot_id = band_name + "Freq";
1722 pot_id = band_name + " Q";
1725 pot_id = band_name + " Shp";
1737 if (!pot_id.empty()) {
1738 pending_display[0] = pot_id;
1740 pending_display[0] = string();
1743 notify_eq_change (param, eq_band, true);
1748 Strip::setup_sends_vpot (boost::shared_ptr<Route> r)
1754 const uint32_t global_pos = _surface->mcp().global_index (*this);
1756 boost::shared_ptr<AutomationControl> pc = r->send_level_controllable (global_pos);
1759 pending_display[0] = string();
1760 pending_display[1] = string();
1764 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_send_level_change, this, BusSendLevel, global_pos, false), ui_context());
1765 _vpot->set_control (pc);
1767 pending_display[0] = PBD::short_version (r->send_name (global_pos), 6);
1769 notify_send_level_change (BusSendLevel, global_pos, true);
1773 Strip::setup_trackview_vpot (boost::shared_ptr<Route> r)
1779 const uint32_t global_pos = _surface->mcp().global_index (*this);
1781 if (global_pos >= 8) {
1782 pending_display[0] = string();
1783 pending_display[1] = string();
1787 boost::shared_ptr<AutomationControl> pc;
1788 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
1791 switch (global_pos) {
1793 pc = r->trim_control ();
1795 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, TrimAutomation, global_pos, false), ui_context());
1796 pending_display[0] = "Trim";
1797 notify_trackview_change (TrimAutomation, global_pos, true);
1802 pc = track->monitoring_control();
1804 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, MonitoringAutomation, global_pos, false), ui_context());
1805 pending_display[0] = "Mon";
1806 notify_trackview_change (MonitoringAutomation, global_pos, true);
1811 pc = r->solo_isolate_control ();
1813 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloIsolateAutomation, global_pos, false), ui_context());
1814 notify_trackview_change (SoloIsolateAutomation, global_pos, true);
1815 pending_display[0] = "S-Iso";
1819 pc = r->solo_safe_control ();
1821 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloSafeAutomation, global_pos, false), ui_context());
1822 notify_trackview_change (SoloSafeAutomation, global_pos, true);
1823 pending_display[0] = "S-Safe";
1827 pc = r->phase_control();
1829 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, PhaseAutomation, global_pos, false), ui_context());
1830 notify_trackview_change (PhaseAutomation, global_pos, true);
1831 pending_display[0] = "Phase";
1835 // pc = r->trim_control ();
1838 // pc = r->trim_control ();
1841 // pc = r->trim_control ();
1846 pending_display[0] = string();
1847 pending_display[1] = string();
1851 _vpot->set_control (pc);
1855 Strip::set_vpot_parameter (AutomationType p)
1857 if (!_route || (p == NullAutomation)) {
1858 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1859 pending_display[1] = string();
1863 boost::shared_ptr<AutomationControl> pan_control;
1865 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1867 reset_saved_values ();
1870 case PanAzimuthAutomation:
1871 pan_control = _route->pan_azimuth_control ();
1873 case PanWidthAutomation:
1874 pan_control = _route->pan_width_control ();
1876 case PanElevationAutomation:
1878 case PanFrontBackAutomation:
1880 case PanLFEAutomation:
1888 _vpot->set_control (pan_control);
1891 pending_display[1] = vpot_mode_string ();
1895 Strip::is_midi_track () const
1897 return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1901 Strip::reset_saved_values ()
1903 _last_pan_azi_position_written = -1.0;
1904 _last_pan_width_position_written = -1.0;
1905 _last_gain_position_written = -1.0;
1906 _last_trim_position_written = -1.0;
1911 Strip::notify_metering_state_changed()
1913 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1917 if (!_route || !_meter) {
1921 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1922 bool metering_active = _surface->mcp().metering_active ();
1924 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1928 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1930 if (!transport_is_rolling || !metering_active) {
1931 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1932 notify_panner_azi_changed (true);
1935 _transport_is_rolling = transport_is_rolling;
1936 _metering_active = metering_active;