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.
27 #include <glibmm/convert.h>
29 #include "midi++/port.h"
31 #include "pbd/compose.h"
32 #include "pbd/convert.h"
34 #include "ardour/amp.h"
35 #include "ardour/bundle.h"
36 #include "ardour/debug.h"
37 #include "ardour/midi_ui.h"
38 #include "ardour/meter.h"
39 #include "ardour/plugin_insert.h"
40 #include "ardour/pannable.h"
41 #include "ardour/panner.h"
42 #include "ardour/panner_shell.h"
43 #include "ardour/rc_configuration.h"
44 #include "ardour/route.h"
45 #include "ardour/session.h"
46 #include "ardour/send.h"
47 #include "ardour/track.h"
48 #include "ardour/midi_track.h"
49 #include "ardour/user_bundle.h"
50 #include "ardour/profile.h"
52 #include "mackie_control_protocol.h"
53 #include "surface_port.h"
64 using namespace ARDOUR;
66 using namespace ArdourSurface;
67 using namespace Mackie;
69 #ifndef timeradd /// only avail with __USE_BSD
70 #define timeradd(a,b,result) \
72 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
73 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
74 if ((result)->tv_usec >= 1000000) \
77 (result)->tv_usec -= 1000000; \
82 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
84 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
97 , _controls_locked (false)
98 , _transport_is_rolling (false)
99 , _metering_active (true)
100 , _block_vpot_mode_redisplay_until (0)
101 , _block_screen_redisplay_until (0)
103 , _pan_mode (PanAzimuthAutomation)
104 , _trim_mode (TrimAutomation)
105 , vpot_parameter (PanAzimuthAutomation)
106 , _last_gain_position_written (-1.0)
107 , _last_pan_azi_position_written (-1.0)
108 , _last_pan_width_position_written (-1.0)
109 , _last_trim_position_written (-1.0)
111 , redisplay_requests (256)
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 control_by_parameter.clear ();
190 control_by_parameter[PanAzimuthAutomation] = (Control*) 0;
191 control_by_parameter[PanWidthAutomation] = (Control*) 0;
192 control_by_parameter[PanElevationAutomation] = (Control*) 0;
193 control_by_parameter[PanFrontBackAutomation] = (Control*) 0;
194 control_by_parameter[PanLFEAutomation] = (Control*) 0;
195 control_by_parameter[GainAutomation] = (Control*) 0;
196 control_by_parameter[TrimAutomation] = (Control*) 0;
197 control_by_parameter[PhaseAutomation] = (Control*) 0;
199 reset_saved_values ();
206 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping route %3\n",
207 _surface->number(), _index, _route->name()));
209 _solo->set_control (_route->solo_control());
210 _mute->set_control (_route->mute_control());
212 _route->solo_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
213 _route->listen_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
215 _route->mute_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
217 if (_route->trim() && route()->trim()->active()) {
218 _route->trim_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trim_changed, this, false), ui_context());
221 if (_route->phase_invert().size()) {
222 _route->phase_invert_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_phase_changed, this, false), ui_context());
223 _route->phase_control()->set_channel(0);
226 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control();
228 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
231 pan_control = _route->pan_width_control();
233 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
236 _route->gain_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
237 _route->PropertyChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
239 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
242 _recenable->set_control (trk->rec_enable_control());
243 trk->rec_enable_control()->Changed .connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
246 // TODO this works when a currently-banked route is made inactive, but not
247 // when a route is activated which should be currently banked.
249 _route->active_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_active_changed, this), ui_context());
250 _route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_route_deleted, this), ui_context());
252 /* setup legal VPot modes for this route */
254 possible_pot_parameters.clear();
256 if (_route->pan_azimuth_control()) {
257 possible_pot_parameters.push_back (PanAzimuthAutomation);
259 if (_route->pan_width_control()) {
260 possible_pot_parameters.push_back (PanWidthAutomation);
262 if (_route->pan_elevation_control()) {
263 possible_pot_parameters.push_back (PanElevationAutomation);
265 if (_route->pan_frontback_control()) {
266 possible_pot_parameters.push_back (PanFrontBackAutomation);
268 if (_route->pan_lfe_control()) {
269 possible_pot_parameters.push_back (PanLFEAutomation);
272 if (_route->trim() && route()->trim()->active()) {
273 possible_pot_parameters.push_back (TrimAutomation);
276 possible_trim_parameters.clear();
278 if (_route->trim() && route()->trim()->active()) {
279 possible_trim_parameters.push_back (TrimAutomation);
280 _trim_mode = TrimAutomation;
283 if (_route->phase_invert().size()) {
284 possible_trim_parameters.push_back (PhaseAutomation);
285 _route->phase_control()->set_channel(0);
286 if (_trim_mode != TrimAutomation) {
287 _trim_mode = PhaseAutomation;
292 _pan_mode = PanAzimuthAutomation;
293 potmode_changed (false);
305 // The active V-pot control may not be active for this strip
306 // But if we zero it in the controls function it may erase
307 // the one we do want
308 _surface->write (_vpot->zero());
310 notify_solo_changed ();
311 notify_mute_changed ();
312 notify_gain_changed ();
313 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
314 notify_panner_azi_changed ();
315 notify_panner_width_changed ();
316 notify_record_enable_changed ();
317 notify_trim_changed ();
318 notify_phase_changed ();
319 notify_processor_changed ();
323 Strip::notify_solo_changed ()
325 if (_route && _solo) {
326 _surface->write (_solo->set_state ((_route->soloed() || _route->listening_via_monitor()) ? on : off));
331 Strip::notify_mute_changed ()
333 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
334 if (_route && _mute) {
335 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\troute muted ? %1\n", _route->muted()));
336 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_route->muted() ? on : off)));
338 _surface->write (_mute->set_state (_route->muted() ? on : off));
343 Strip::notify_record_enable_changed ()
345 if (_route && _recenable) {
346 _surface->write (_recenable->set_state (_route->record_enabled() ? on : off));
351 Strip::notify_active_changed ()
353 _surface->mcp().refresh_current_bank();
357 Strip::notify_route_deleted ()
359 _surface->mcp().refresh_current_bank();
363 Strip::notify_gain_changed (bool force_update)
369 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
375 boost::shared_ptr<AutomationControl> ac = _route->gain_control();
377 float gain_coefficient = ac->get_value();
378 float normalized_position = ac->internal_to_interface (gain_coefficient);
381 if (force_update || normalized_position != _last_gain_position_written) {
383 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
384 if (!control->in_use()) {
385 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
387 queue_parameter_display (GainAutomation, gain_coefficient);
389 if (!control->in_use()) {
390 _surface->write (_fader->set_position (normalized_position));
392 queue_parameter_display (GainAutomation, gain_coefficient);
395 _last_gain_position_written = normalized_position;
401 Strip::notify_trim_changed (bool force_update)
405 if (!_route->trim() || !route()->trim()->active()) {
408 Control* control = 0;
409 ControlParameterMap::iterator i = control_by_parameter.find (TrimAutomation);
411 if (i == control_by_parameter.end()) {
417 boost::shared_ptr<AutomationControl> ac = _route->trim_control();
419 float gain_coefficient = ac->get_value();
420 float normalized_position = ac->internal_to_interface (gain_coefficient);
422 if (force_update || normalized_position != _last_trim_position_written) {
423 if (control == _fader) {
424 if (!_fader->in_use()) {
425 _surface->write (_fader->set_position (normalized_position));
426 queue_parameter_display (TrimAutomation, gain_coefficient);
428 } else if (control == _vpot) {
429 _surface->write (_vpot->set (normalized_position, true, Pot::dot));
430 queue_parameter_display (TrimAutomation, gain_coefficient);
432 _last_trim_position_written = normalized_position;
438 Strip::notify_phase_changed (bool force_update)
441 if (!_route->phase_invert().size()) {
445 Control* control = 0;
446 ControlParameterMap::iterator i = control_by_parameter.find (PhaseAutomation);
448 if (i == control_by_parameter.end()) {
454 float normalized_position = _route->phase_control()->get_value();
456 if (control == _fader) {
457 if (!_fader->in_use()) {
458 _surface->write (_fader->set_position (normalized_position));
459 queue_parameter_display (PhaseAutomation, normalized_position);
461 } else if (control == _vpot) {
462 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
463 queue_parameter_display (PhaseAutomation, normalized_position);
469 Strip::notify_processor_changed (bool force_update)
474 Strip::notify_property_changed (const PropertyChange& what_changed)
476 if (!what_changed.contains (ARDOUR::Properties::name)) {
484 Strip::show_route_name ()
486 MackieControlProtocol::SubViewMode svm = _surface->mcp().subview_mode();
488 if (svm != MackieControlProtocol::None) {
489 /* subview mode is responsible for upper line */
493 string fullname = string();
495 // make sure first three strips get cleared of view mode
500 fullname = _route->name();
504 if (fullname.length() <= 6) {
507 line1 = PBD::short_version (fullname, 6);
510 _surface->write (display (0, line1));
514 Strip::notify_eq_change (AutomationType type, uint32_t band, bool force_update)
516 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
519 /* not in subview mode */
523 if (_surface->mcp().subview_mode() != MackieControlProtocol::EQ) {
524 /* no longer in EQ subview mode */
528 boost::shared_ptr<AutomationControl> control;
532 control = r->eq_gain_controllable (band);
535 control = r->eq_freq_controllable (band);
538 control = r->eq_q_controllable (band);
541 control = r->eq_shape_controllable (band);
544 control = r->eq_hpf_controllable ();
547 control = r->eq_enable_controllable ();
554 float val = control->get_value();
555 queue_parameter_display (type, val);
556 /* update pot/encoder */
557 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
562 Strip::notify_dyn_change (AutomationType type, bool force_update, bool propagate_mode)
564 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
567 /* not in subview mode */
571 if (_surface->mcp().subview_mode() != MackieControlProtocol::Dynamics) {
572 /* no longer in EQ subview mode */
576 boost::shared_ptr<AutomationControl> control;
577 bool reset_all = false;
581 control = r->comp_threshold_controllable ();
584 control = r->comp_speed_controllable ();
587 control = r->comp_mode_controllable ();
591 control = r->comp_makeup_controllable ();
594 control = r->comp_redux_controllable ();
597 control = r->comp_enable_controllable ();
603 if (propagate_mode && reset_all) {
604 _surface->subview_mode_changed ();
608 float val = control->get_value();
609 queue_parameter_display (type, val);
610 /* update pot/encoder */
611 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
616 Strip::notify_panner_azi_changed (bool force_update)
622 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
624 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
630 Control* control = 0;
631 ControlParameterMap::iterator i = control_by_parameter.find (PanAzimuthAutomation);
633 if (i == control_by_parameter.end()) {
639 double normalized_pos = pan_control->internal_to_interface (pan_control->get_value());
640 double internal_pos = pan_control->get_value();
642 if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
644 if (control == _fader) {
645 if (!_fader->in_use()) {
646 _surface->write (_fader->set_position (normalized_pos));
647 /* show actual internal value to user */
648 queue_parameter_display (PanAzimuthAutomation, internal_pos);
650 } else if (control == _vpot) {
651 _surface->write (_vpot->set (normalized_pos, true, Pot::dot));
652 /* show actual internal value to user */
653 queue_parameter_display (PanAzimuthAutomation, internal_pos);
656 _last_pan_azi_position_written = normalized_pos;
661 Strip::notify_panner_width_changed (bool force_update)
667 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
669 boost::shared_ptr<AutomationControl> pan_control = _route->pan_width_control ();
675 Control* control = 0;
676 ControlParameterMap::iterator i = control_by_parameter.find (PanWidthAutomation);
678 if (i == control_by_parameter.end()) {
684 double pos = pan_control->internal_to_interface (pan_control->get_value());
686 if (force_update || pos != _last_pan_width_position_written) {
688 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
690 if (control == _fader) {
691 if (!control->in_use()) {
692 _surface->write (_fader->set_position (pos));
693 queue_parameter_display (PanWidthAutomation, pos);
697 } else if (control == _vpot) {
698 _surface->write (_vpot->set (pos, true, Pot::spread));
699 queue_parameter_display (PanWidthAutomation, pos);
702 _last_pan_width_position_written = pos;
707 Strip::select_event (Button&, ButtonState bs)
709 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
713 int ms = _surface->mcp().main_modifier_state();
715 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
716 _controls_locked = !_controls_locked;
717 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
718 block_vpot_mode_display_for (1000);
722 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
723 /* reset to default */
724 boost::shared_ptr<AutomationControl> ac = _fader->control ();
726 ac->set_value (ac->normal(), Controllable::NoGroup);
731 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
732 _surface->mcp().add_down_select_button (_surface->number(), _index);
733 _surface->mcp().select_range ();
736 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
737 _surface->mcp().remove_down_select_button (_surface->number(), _index);
742 Strip::vselect_event (Button&, ButtonState bs)
744 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
746 /* subview mode: vpot press acts like a button for toggle parameters */
752 boost::shared_ptr<AutomationControl> control = _vpot->control ();
757 if (control->toggled()) {
758 if (control->toggled()) {
759 control->set_value (!control->get_value(), Controllable::NoGroup);
768 int ms = _surface->mcp().main_modifier_state();
770 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
772 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
776 /* reset to default/normal value */
777 ac->set_value (ac->normal(), Controllable::NoGroup);
782 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
790 Strip::fader_touch_event (Button&, ButtonState bs)
792 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
796 boost::shared_ptr<AutomationControl> ac = _fader->control ();
798 _fader->set_in_use (true);
799 _fader->start_touch (_surface->mcp().transport_frame());
802 queue_parameter_display ((AutomationType) ac->parameter().type(), ac->get_value());
807 _fader->set_in_use (false);
808 _fader->stop_touch (_surface->mcp().transport_frame(), true);
815 Strip::handle_button (Button& button, ButtonState bs)
817 boost::shared_ptr<AutomationControl> control;
820 button.set_in_use (true);
822 button.set_in_use (false);
825 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
827 switch (button.bid()) {
829 select_event (button, bs);
832 case Button::VSelect:
833 vselect_event (button, bs);
836 case Button::FaderTouch:
837 fader_touch_event (button, bs);
841 if ((control = button.control ())) {
843 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
844 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
846 float new_value = control->get_value() ? 0.0 : 1.0;
848 /* get all controls that either have their
849 * button down or are within a range of
850 * several down buttons
853 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type());
856 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
857 controls.size(), control->parameter().type(), new_value));
859 /* apply change, with potential modifier semantics */
861 Controllable::GroupControlDisposition gcd;
863 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
864 gcd = Controllable::NoGroup;
866 gcd = Controllable::UseGroup;
869 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
870 (*c)->set_value (new_value, gcd);
874 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
875 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
883 Strip::queue_parameter_display (AutomationType type, float val)
885 RedisplayRequest req;
890 redisplay_requests.write (&req, 1);
894 Strip::do_parameter_display (AutomationType type, float val)
896 bool screen_hold = false;
902 _surface->write (display (1, " -inf "));
904 float dB = accurate_coefficient_to_dB (val);
905 snprintf (buf, sizeof (buf), "%6.1f", dB);
906 _surface->write (display (1, buf));
911 case PanAzimuthAutomation:
912 if (Profile->get_mixbus()) {
913 snprintf (buf, sizeof (buf), "%2.1f", val);
914 _surface->write (display (1, buf));
918 boost::shared_ptr<Pannable> p = _route->pannable();
919 if (p && _route->panner()) {
920 string str =_route->panner()->value_as_string (p->pan_azimuth_control);
921 _surface->write (display (1, str));
928 case PanWidthAutomation:
930 snprintf (buf, sizeof (buf), "%5ld%%", lrintf ((val * 200.0)-100));
931 _surface->write (display (1, buf));
938 float dB = accurate_coefficient_to_dB (val);
939 snprintf (buf, sizeof (buf), "%6.1f", dB);
940 _surface->write (display (1, buf));
945 case PhaseAutomation:
947 if (_route->phase_control()->get_value() < 0.5) {
948 _surface->write (display (1, "Normal"));
950 _surface->write (display (1, "Invert"));
965 snprintf (buf, sizeof (buf), "%6.1f", val);
966 _surface->write (display (1, buf));
972 _surface->write (display (1, "on"));
974 _surface->write (display (1, "off"));
978 if (_surface->mcp().subview_route()) {
979 _surface->write (display (1, _surface->mcp().subview_route()->comp_mode_name (val)));
987 block_vpot_mode_display_for (1000);
992 Strip::handle_fader_touch (Fader& fader, bool touch_on)
995 fader.start_touch (_surface->mcp().transport_frame());
997 fader.stop_touch (_surface->mcp().transport_frame(), false);
1002 Strip::handle_fader (Fader& fader, float position)
1004 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
1005 boost::shared_ptr<AutomationControl> ac = fader.control();
1010 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
1012 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1013 gcd = Controllable::NoGroup;
1016 fader.set_value (position, gcd);
1018 /* From the Mackie Control MIDI implementation docs:
1020 In order to ensure absolute synchronization with the host software,
1021 Mackie Control uses a closed-loop servo system for the faders,
1022 meaning the faders will always move to their last received position.
1023 When a host receives a Fader Position Message, it must then
1024 re-transmit that message to the Mackie Control or else the faders
1025 will return to their last position.
1028 _surface->write (fader.set_position (position));
1032 Strip::handle_pot (Pot& pot, float delta)
1034 /* Pots only emit events when they move, not when they
1035 stop moving. So to get a stop event, we need to use a timeout.
1038 boost::shared_ptr<AutomationControl> ac = pot.control();
1042 double p = pot.get_value ();
1044 // fader and pot should be the same and fader is hard coded 0 -> 1
1051 Strip::periodic (ARDOUR::microseconds_t now)
1053 bool reshow_vpot_mode = false;
1054 bool reshow_name = false;
1055 bool good_strip = true;
1058 // view mode may cover as many as 3 strips
1059 // needs to be cleared when there are less than 3 routes
1067 if (_block_screen_redisplay_until >= now) {
1068 if (_surface->mcp().device_info().has_separate_meters()) {
1071 /* no drawing here, for now */
1074 } else if (_block_screen_redisplay_until) {
1076 /* timeout reached, reset */
1078 _block_screen_redisplay_until = 0;
1079 reshow_vpot_mode = (true && good_strip);
1083 if (_block_vpot_mode_redisplay_until >= now) {
1085 } else if (_block_vpot_mode_redisplay_until) {
1087 /* timeout reached, reset */
1089 _block_vpot_mode_redisplay_until = 0;
1090 reshow_vpot_mode = (true && good_strip);
1097 if (reshow_vpot_mode) {
1098 return_to_vpot_mode_display ();
1099 } else if (good_strip) {
1100 /* no point doing this if we just switched back to vpot mode
1102 update_automation ();
1112 Strip::redisplay (ARDOUR::microseconds_t now)
1114 RedisplayRequest req;
1115 bool have_request = false;
1117 while (redisplay_requests.read (&req, 1) == 1) {
1119 have_request = true;
1122 if (_block_screen_redisplay_until >= now) {
1127 do_parameter_display (req.type, req.val);
1132 Strip::update_automation ()
1138 ARDOUR::AutoState state = _route->gain_control()->automation_state();
1140 if (state == Touch || state == Play) {
1141 notify_gain_changed (false);
1144 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
1146 state = pan_control->automation_state ();
1147 if (state == Touch || state == Play) {
1148 notify_panner_azi_changed (false);
1152 pan_control = _route->pan_width_control ();
1154 state = pan_control->automation_state ();
1155 if (state == Touch || state == Play) {
1156 notify_panner_width_changed (false);
1160 if (_route->trim() && route()->trim()->active()) {
1161 ARDOUR::AutoState trim_state = _route->trim_control()->automation_state();
1162 if (trim_state == Touch || trim_state == Play) {
1163 notify_trim_changed (false);
1169 Strip::update_meter ()
1171 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1175 if (_meter && _transport_is_rolling && _metering_active) {
1176 float dB = const_cast<PeakMeter&> (_route->peak_meter()).meter_level (0, MeterMCP);
1177 _meter->send_update (*_surface, dB);
1184 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
1185 _surface->write ((*it)->zero ());
1188 _surface->write (blank_display (0));
1189 _surface->write (blank_display (1));
1193 Strip::blank_display (uint32_t line_number)
1195 return display (line_number, string());
1199 Strip::display (uint32_t line_number, const std::string& line)
1201 assert (line_number <= 1);
1203 MidiByteArray retval;
1205 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
1208 retval << _surface->sysex_hdr();
1212 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
1213 retval << (_index * 7 + (line_number * 0x38));
1215 // ascii data to display. @param line is UTF-8
1216 string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
1217 string::size_type len = ascii.length();
1219 ascii = ascii.substr (0, 6);
1223 // pad with " " out to 6 chars
1224 for (int i = len; i < 6; ++i) {
1228 // column spacer, unless it's the right-hand column
1234 retval << MIDI::eox;
1240 Strip::lock_controls ()
1242 _controls_locked = true;
1246 Strip::unlock_controls ()
1248 _controls_locked = false;
1252 Strip::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& rl)
1254 for (ARDOUR::StrongRouteNotificationList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
1255 if ((*i) == _route) {
1256 _surface->write (_select->set_state (on));
1261 _surface->write (_select->set_state (off));
1265 Strip::vpot_mode_string ()
1267 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1272 if (control_by_parameter.find (GainAutomation)->second == _vpot) {
1274 } else if (control_by_parameter.find (TrimAutomation)->second == _vpot) {
1276 } else if (control_by_parameter.find (PhaseAutomation)->second == _vpot) {
1277 return string_compose ("Phase%1", _route->phase_control()->channel() + 1);
1278 } else if (control_by_parameter.find (PanAzimuthAutomation)->second == _vpot) {
1280 } else if (control_by_parameter.find (PanWidthAutomation)->second == _vpot) {
1282 } else if (control_by_parameter.find (PanElevationAutomation)->second == _vpot) {
1284 } else if (control_by_parameter.find (PanFrontBackAutomation)->second == _vpot) {
1286 } else if (control_by_parameter.find (PanLFEAutomation)->second == _vpot) {
1290 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1298 Strip::potmode_changed (bool notify)
1305 int pm = _surface->mcp().pot_mode();
1307 case MackieControlProtocol::Pan:
1308 // This needs to set current pan mode (azimuth or width... or whatever)
1309 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Assign pot to Pan mode %1\n", enum_2_string (_pan_mode)));
1310 set_vpot_parameter (_pan_mode);
1312 case MackieControlProtocol::Trim:
1313 DEBUG_TRACE (DEBUG::MackieControl, "Assign pot to Trim mode.\n");
1314 set_vpot_parameter (_trim_mode);
1324 Strip::block_screen_display_for (uint32_t msecs)
1326 _block_screen_redisplay_until = ARDOUR::get_microseconds() + (msecs * 1000);
1330 Strip::block_vpot_mode_display_for (uint32_t msecs)
1332 _block_vpot_mode_redisplay_until = ARDOUR::get_microseconds() + (msecs * 1000);
1336 Strip::return_to_vpot_mode_display ()
1338 /* returns the second line of the two-line per-strip display
1339 back the mode where it shows what the VPot controls.
1342 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1343 /* do nothing - second line shows value of current subview parameter */
1345 } else if (_route) {
1346 _surface->write (display (1, vpot_mode_string()));
1348 _surface->write (blank_display (1));
1353 Strip::next_pot_mode ()
1355 vector<AutomationType>::iterator i;
1357 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1358 /* do not change vpot mode while in flipped mode */
1359 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1360 _surface->write (display (1, "Flip"));
1361 block_vpot_mode_display_for (1000);
1366 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1373 if (_surface->mcp().pot_mode() == MackieControlProtocol::Pan) {
1375 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1379 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1380 if ((*i) == ac->parameter().type()) {
1385 /* move to the next mode in the list, or back to the start (which will
1386 also happen if the current mode is not in the current pot mode list)
1389 if (i != possible_pot_parameters.end()) {
1393 if (i == possible_pot_parameters.end()) {
1394 i = possible_pot_parameters.begin();
1397 set_vpot_parameter (*i);
1398 } else if (_surface->mcp().pot_mode() == MackieControlProtocol::Trim) {
1399 if (possible_trim_parameters.empty() || (possible_trim_parameters.size() == 1 && possible_trim_parameters.front() == ac->parameter().type())) {
1403 for (i = possible_trim_parameters.begin(); i != possible_trim_parameters.end(); ++i) {
1404 if ((*i) == ac->parameter().type()) {
1408 if ((*i) == PhaseAutomation && _route->phase_invert().size() > 1) {
1409 // There are more than one channel of phase
1410 if ((_route->phase_control()->channel() + 1) < _route->phase_invert().size()) {
1411 _route->phase_control()->set_channel(_route->phase_control()->channel() + 1);
1412 set_vpot_parameter (*i);
1415 _route->phase_control()->set_channel(0);
1418 /* move to the next mode in the list, or back to the start (which will
1419 also happen if the current mode is not in the current pot mode list)
1422 if (i != possible_trim_parameters.end()) {
1426 if (i == possible_trim_parameters.end()) {
1427 i = possible_trim_parameters.begin();
1429 set_vpot_parameter (*i);
1434 Strip::subview_mode_changed ()
1436 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
1438 subview_connections.drop_connections ();
1440 switch (_surface->mcp().subview_mode()) {
1441 case MackieControlProtocol::None:
1442 set_vpot_parameter (vpot_parameter);
1443 notify_metering_state_changed ();
1447 case MackieControlProtocol::EQ:
1451 /* leave it as it was */
1455 case MackieControlProtocol::Dynamics:
1459 /* leave it as it was */
1466 Strip::setup_dyn_vpot (boost::shared_ptr<Route> r)
1472 boost::shared_ptr<AutomationControl> tc = r->comp_threshold_controllable ();
1473 boost::shared_ptr<AutomationControl> sc = r->comp_speed_controllable ();
1474 boost::shared_ptr<AutomationControl> mc = r->comp_mode_controllable ();
1475 boost::shared_ptr<AutomationControl> kc = r->comp_makeup_controllable ();
1476 boost::shared_ptr<AutomationControl> rc = r->comp_redux_controllable ();
1477 boost::shared_ptr<AutomationControl> ec = r->comp_enable_controllable ();
1479 uint32_t pos = _surface->mcp().global_index (*this);
1481 /* we will control the pos-th available parameter, from the list in the
1482 * order shown above.
1485 vector<boost::shared_ptr<AutomationControl> > available;
1486 vector<AutomationType> params;
1488 if (tc) { available.push_back (tc); params.push_back (CompThreshold); }
1489 if (sc) { available.push_back (sc); params.push_back (CompSpeed); }
1490 if (mc) { available.push_back (mc); params.push_back (CompMode); }
1491 if (kc) { available.push_back (kc); params.push_back (CompMakeup); }
1492 if (rc) { available.push_back (rc); params.push_back (CompRedux); }
1493 if (ec) { available.push_back (ec); params.push_back (CompEnable); }
1495 if (pos >= available.size()) {
1496 /* this knob is not needed to control the available parameters */
1497 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1498 _surface->write (display (0, string()));
1499 _surface->write (display (1, string()));
1503 boost::shared_ptr<AutomationControl> pc;
1504 AutomationType param;
1506 pc = available[pos];
1507 param = params[pos];
1509 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_dyn_change, this, param, false, true), ui_context());
1510 _vpot->set_control (pc);
1520 pot_id = r->comp_speed_name (mc->get_value());
1541 if (!pot_id.empty()) {
1542 _surface->write (display (0, pot_id));
1545 notify_dyn_change (param, true, false);
1549 Strip::setup_eq_vpot (boost::shared_ptr<Route> r)
1551 uint32_t bands = r->eq_band_cnt ();
1554 /* should never get here */
1558 /* figure out how many params per band are available */
1560 boost::shared_ptr<AutomationControl> pc;
1561 uint32_t params_per_band = 0;
1563 if ((pc = r->eq_gain_controllable (0))) {
1564 params_per_band += 1;
1566 if ((pc = r->eq_freq_controllable (0))) {
1567 params_per_band += 1;
1569 if ((pc = r->eq_q_controllable (0))) {
1570 params_per_band += 1;
1572 if ((pc = r->eq_shape_controllable (0))) {
1573 params_per_band += 1;
1576 /* pick the one for this strip, based on its global position across
1582 const uint32_t total_band_parameters = bands * params_per_band;
1583 const uint32_t global_pos = _surface->mcp().global_index (*this);
1584 AutomationType param = NullAutomation;
1589 if (global_pos < total_band_parameters) {
1591 /* show a parameter for an EQ band */
1593 const uint32_t parameter = global_pos % params_per_band;
1594 eq_band = global_pos / params_per_band;
1595 band_name = r->eq_band_name (eq_band);
1597 switch (parameter) {
1599 pc = r->eq_gain_controllable (eq_band);
1603 pc = r->eq_freq_controllable (eq_band);
1604 param = EQFrequency;
1607 pc = r->eq_q_controllable (eq_band);
1611 pc = r->eq_shape_controllable (eq_band);
1618 /* show a non-band parameter (HPF or enable)
1621 uint32_t parameter = global_pos - total_band_parameters;
1623 switch (parameter) {
1624 case 0: /* first control after band parameters */
1625 pc = r->eq_hpf_controllable();
1628 case 1: /* second control after band parameters */
1629 pc = r->eq_enable_controllable();
1633 /* nothing to control */
1634 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1635 _surface->write (display (0, string()));
1636 _surface->write (display (1, string()));
1645 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_eq_change, this, param, eq_band, false), ui_context());
1646 _vpot->set_control (pc);
1652 pot_id = band_name + "Gain";
1655 pot_id = band_name + "Freq";
1658 pot_id = band_name + " Q";
1661 pot_id = band_name + " Shp";
1673 if (!pot_id.empty()) {
1674 _surface->write (display (0, pot_id));
1677 notify_eq_change (param, eq_band, true);
1682 Strip::set_vpot_parameter (AutomationType p)
1684 if (!_route || (p == NullAutomation)) {
1685 control_by_parameter[vpot_parameter] = 0;
1686 vpot_parameter = NullAutomation;
1687 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1688 _surface->write (display (1, string()));
1692 boost::shared_ptr<AutomationControl> pan_control;
1694 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1696 reset_saved_values ();
1698 /* unset any mapping between the vpot and any existing parameters */
1700 for (ControlParameterMap::iterator i = control_by_parameter.begin(); i != control_by_parameter.end(); ++i) {
1702 if (i != control_by_parameter.end() && i->second == _vpot) {
1708 case PanAzimuthAutomation:
1709 if ((pan_control = _route->pan_azimuth_control ())) {
1710 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1711 _pan_mode = PanAzimuthAutomation;
1712 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1713 /* gain to vpot, pan azi to fader */
1714 _vpot->set_control (_route->gain_control());
1715 vpot_parameter = GainAutomation;
1716 control_by_parameter[GainAutomation] = _vpot;
1717 _fader->set_control (pan_control);
1718 control_by_parameter[PanAzimuthAutomation] = _fader;
1720 _fader->set_control (boost::shared_ptr<AutomationControl>());
1721 control_by_parameter[PanAzimuthAutomation] = 0;
1724 /* gain to fader, pan azi to vpot */
1725 vpot_parameter = PanAzimuthAutomation;
1726 _fader->set_control (_route->gain_control());
1727 control_by_parameter[GainAutomation] = _fader;
1728 _vpot->set_control (pan_control);
1729 control_by_parameter[PanAzimuthAutomation] = _vpot;
1732 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1733 control_by_parameter[PanAzimuthAutomation] = 0;
1737 case PanWidthAutomation:
1738 if ((pan_control = _route->pan_width_control ())) {
1739 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1740 _pan_mode = PanWidthAutomation;
1741 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1742 /* gain to vpot, pan width to fader */
1743 _vpot->set_control (_route->gain_control());
1744 vpot_parameter = GainAutomation;
1745 control_by_parameter[GainAutomation] = _vpot;
1746 _fader->set_control (pan_control);
1747 control_by_parameter[PanWidthAutomation] = _fader;
1749 _fader->set_control (boost::shared_ptr<AutomationControl>());
1750 control_by_parameter[PanWidthAutomation] = 0;
1753 /* gain to fader, pan width to vpot */
1754 vpot_parameter = PanWidthAutomation;
1755 _fader->set_control (_route->gain_control());
1756 control_by_parameter[GainAutomation] = _fader;
1757 _vpot->set_control (pan_control);
1758 control_by_parameter[PanWidthAutomation] = _vpot;
1761 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1762 control_by_parameter[PanWidthAutomation] = 0;
1766 case PanElevationAutomation:
1768 case PanFrontBackAutomation:
1770 case PanLFEAutomation:
1772 case TrimAutomation:
1773 _trim_mode = TrimAutomation;
1774 vpot_parameter = TrimAutomation;
1775 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1776 /* gain to vpot, trim to fader */
1777 _vpot->set_control (_route->gain_control());
1778 control_by_parameter[GainAutomation] = _vpot;
1779 if (_route->trim() && route()->trim()->active()) {
1780 _fader->set_control (_route->trim_control());
1781 control_by_parameter[TrimAutomation] = _fader;
1783 _fader->set_control (boost::shared_ptr<AutomationControl>());
1784 control_by_parameter[TrimAutomation] = 0;
1787 /* gain to fader, trim to vpot */
1788 _fader->set_control (_route->gain_control());
1789 control_by_parameter[GainAutomation] = _fader;
1790 if (_route->trim() && route()->trim()->active()) {
1791 _vpot->set_control (_route->trim_control());
1792 control_by_parameter[TrimAutomation] = _vpot;
1794 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1795 control_by_parameter[TrimAutomation] = 0;
1799 case PhaseAutomation:
1800 _trim_mode = PhaseAutomation;
1801 vpot_parameter = PhaseAutomation;
1802 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1803 /* gain to vpot, phase to fader */
1804 _vpot->set_control (_route->gain_control());
1805 control_by_parameter[GainAutomation] = _vpot;
1806 if (_route->phase_invert().size()) {
1807 _fader->set_control (_route->phase_control());
1808 control_by_parameter[PhaseAutomation] = _fader;
1810 _fader->set_control (boost::shared_ptr<AutomationControl>());
1811 control_by_parameter[PhaseAutomation] = 0;
1814 /* gain to fader, phase to vpot */
1815 _fader->set_control (_route->gain_control());
1816 control_by_parameter[GainAutomation] = _fader;
1817 if (_route->phase_invert().size()) {
1818 _vpot->set_control (_route->phase_control());
1819 control_by_parameter[PhaseAutomation] = _vpot;
1821 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1822 control_by_parameter[PhaseAutomation] = 0;
1827 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("vpot mode %1 not known.\n", p));
1832 _surface->write (display (1, vpot_mode_string()));
1836 Strip::is_midi_track () const
1838 return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1842 Strip::reset_saved_values ()
1844 _last_pan_azi_position_written = -1.0;
1845 _last_pan_width_position_written = -1.0;
1846 _last_gain_position_written = -1.0;
1847 _last_trim_position_written = -1.0;
1852 Strip::notify_metering_state_changed()
1854 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1858 if (!_route || !_meter) {
1862 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1863 bool metering_active = _surface->mcp().metering_active ();
1865 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1869 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1871 if (!transport_is_rolling || !metering_active) {
1872 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1873 notify_panner_azi_changed (true);
1876 _transport_is_rolling = transport_is_rolling;
1877 _metering_active = metering_active;