2 * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "ardour/automation_control.h"
20 #include "ardour/gain_control.h"
21 #include "ardour/meter.h"
22 #include "ardour/plugin_insert.h"
23 #include "ardour/session.h"
24 #include "ardour/stripable.h"
25 #include "ardour/track.h"
26 #include "ardour/value_as_string.h"
28 #include "control_protocol/control_protocol.h"
30 #include "fp8_strip.h"
32 using namespace ARDOUR;
33 using namespace ArdourSurface;
34 using namespace ArdourSurface::FP8Types;
36 FP8Strip::FP8Strip (FP8Base& b, uint8_t id)
39 , _solo (b, 0x08 + id)
40 , _mute (b, 0x10 + id)
41 , _selrec (b, 0x18 + id, true)
45 , _displaymode (Stripables)
50 _last_meter = _last_redux = _last_barpos = 0xff;
52 _mute.StateChange.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_mute, this, _1));
53 _solo.StateChange.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_solo, this, _1));
54 select_button ().released.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_select, this));
55 recarm_button ().released.connect_same_thread (_button_connections, boost::bind (&FP8Strip::set_recarm, this));
56 b.Periodic.connect_same_thread (_base_connection, boost::bind (&FP8Strip::periodic, this));
59 FP8Strip::~FP8Strip ()
61 _fader_connection.disconnect ();
62 _mute_connection.disconnect ();
63 _solo_connection.disconnect ();
64 _rec_connection.disconnect ();
65 _pan_connection.disconnect ();
73 _base_connection.disconnect ();
74 _button_connections.drop_connections ();
78 FP8Strip::initialize ()
80 /* this is called once midi transmission is possible,
81 * ie from FaderPort8::connected()
83 _solo.set_active (false);
84 _mute.set_active (false);
86 /* reset momentary button state */
90 /* clear cached values */
92 _last_meter = _last_redux = _last_barpos = 0xff;
94 select_button ().set_color (0xffffffff);
95 select_button ().set_active (false);
96 select_button ().set_blinking (false);
98 recarm_button ().set_active (false);
99 recarm_button ().set_color (0xffffffff);
101 set_strip_mode (0, true);
104 _last_line[0].clear ();
105 _last_line[1].clear ();
106 _last_line[2].clear ();
107 _last_line[3].clear ();
108 _base.tx_sysex (4, 0x12, _id, 0x00, 0x00);
109 _base.tx_sysex (4, 0x12, _id, 0x01, 0x00);
110 _base.tx_sysex (4, 0x12, _id, 0x02, 0x00);
111 _base.tx_sysex (4, 0x12, _id, 0x03, 0x00);
113 set_bar_mode (4); // off
115 _base.tx_midi2 (0xd0 + _id, 0); // reset meter
116 _base.tx_midi2 (0xd8 + _id, 0); // reset redux
118 _base.tx_midi3 (0xe0 + _id, 0, 0); // fader
122 #define GENERATE_SET_CTRL_FUNCTION(NAME) \
124 FP8Strip::set_ ##NAME##_controllable (boost::shared_ptr<AutomationControl> ac) \
126 if (_##NAME##_ctrl == ac) { \
129 _##NAME##_connection.disconnect(); \
130 _##NAME##_ctrl = ac; \
133 ac->Changed.connect (_##NAME##_connection, MISSING_INVALIDATOR, \
134 boost::bind (&FP8Strip::notify_##NAME##_changed, this), fp8_context()); \
136 notify_##NAME##_changed (); \
140 GENERATE_SET_CTRL_FUNCTION (fader)
141 GENERATE_SET_CTRL_FUNCTION (mute)
142 GENERATE_SET_CTRL_FUNCTION (solo)
143 GENERATE_SET_CTRL_FUNCTION (rec)
144 GENERATE_SET_CTRL_FUNCTION (pan)
145 GENERATE_SET_CTRL_FUNCTION (x_select)
147 #undef GENERATE_SET_CTRL_FUNCTION
149 // special case -- w/_select_plugin_functor
151 FP8Strip::set_select_controllable (boost::shared_ptr<AutomationControl> ac)
153 _select_plugin_functor.clear ();
154 set_x_select_controllable (ac);
158 FP8Strip::set_select_cb (boost::function<void ()>& functor)
160 set_select_controllable (boost::shared_ptr<AutomationControl>());
161 _select_plugin_functor = functor;
165 FP8Strip::unset_controllables (int which)
167 _peak_meter = boost::shared_ptr<ARDOUR::PeakMeter>();
168 _redux_ctrl = boost::shared_ptr<ARDOUR::ReadOnlyControl>();
170 if (which & CTRL_FADER) {
171 set_fader_controllable (boost::shared_ptr<AutomationControl>());
173 if (which & CTRL_MUTE) {
174 set_mute_controllable (boost::shared_ptr<AutomationControl>());
176 if (which & CTRL_SOLO) {
177 set_solo_controllable (boost::shared_ptr<AutomationControl>());
179 if (which & CTRL_REC) {
180 set_rec_controllable (boost::shared_ptr<AutomationControl>());
182 if (which & CTRL_PAN) {
183 set_pan_controllable (boost::shared_ptr<AutomationControl>());
185 if (which & CTRL_SELECT) {
186 set_select_controllable (boost::shared_ptr<AutomationControl>());
187 select_button ().set_color (0xffffffff);
188 select_button ().set_active (false);
189 select_button ().set_blinking (false);
191 if (which & CTRL_TEXT0) {
192 set_text_line (0x00, "");
194 if (which & CTRL_TEXT1) {
195 set_text_line (0x01, "");
197 if (which & CTRL_TEXT2) {
198 set_text_line (0x02, "");
200 if (which & CTRL_TEXT3) {
201 set_text_line (0x03, "");
203 set_bar_mode (4); // Off
207 FP8Strip::set_stripable (boost::shared_ptr<Stripable> s, bool panmode)
212 set_fader_controllable (s->pan_azimuth_control ());
214 set_fader_controllable (s->gain_control ());
216 set_pan_controllable (s->pan_azimuth_control ());
218 if (s->is_monitor ()) {
219 set_mute_controllable (boost::shared_ptr<AutomationControl>());
221 set_mute_controllable (s->mute_control ());
223 set_solo_controllable (s->solo_control ());
225 if (boost::dynamic_pointer_cast<Track> (s)) {
226 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
227 set_rec_controllable (t->rec_enable_control ());
228 recarm_button ().set_color (0xff0000ff);
230 set_rec_controllable (boost::shared_ptr<AutomationControl>());
231 recarm_button ().set_color (0xffffffff);
232 recarm_button ().set_active (false);
234 _peak_meter = s->peak_meter ();
235 _redux_ctrl = s->comp_redux_controllable ();
237 set_select_controllable (boost::shared_ptr<AutomationControl>());
238 select_button ().set_active (s->is_selected ());
239 select_button ().set_color (s->presentation_info ().color());
240 //select_button ().set_blinking (false);
242 set_strip_mode (0x05);
243 set_text_line (0x00, s->name ());
244 set_text_line (0x01, _pan_ctrl ? _pan_ctrl->get_user_string () : "");
245 set_text_line (0x02, "");
246 set_text_line (0x03, "");
249 /* *****************************************************************************
250 * Parse Strip Specifig MIDI Events
254 FP8Strip::midi_touch (bool t)
257 boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
262 if (!ac->touching ()) {
263 ac->start_touch (ac->session().transport_frame());
266 ac->stop_touch (true, ac->session().transport_frame());
272 FP8Strip::midi_fader (float val)
274 assert (val >= 0.f && val <= 1.f);
278 boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
282 if (ac->automation_state() == Touch && !ac->touching ()) {
283 ac->start_touch (ac->session().transport_frame());
285 ac->set_value (ac->interface_to_internal (val), group_mode ());
289 /* *****************************************************************************
290 * Actions from Controller, Update Model
293 PBD::Controllable::GroupControlDisposition
294 FP8Strip::group_mode () const
296 if (_base.shift_mod ()) {
297 return PBD::Controllable::InverseGroup;
299 return PBD::Controllable::UseGroup;
304 FP8Strip::set_mute (bool on)
307 if (_mute_ctrl->automation_state() == Touch && !_mute_ctrl->touching ()) {
308 _mute_ctrl->start_touch (_mute_ctrl->session().transport_frame());
310 _mute_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
315 FP8Strip::set_solo (bool on)
318 if (_solo_ctrl->automation_state() == Touch && !_solo_ctrl->touching ()) {
319 _solo_ctrl->start_touch (_solo_ctrl->session().transport_frame());
321 _solo_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
326 FP8Strip::set_recarm ()
329 const bool on = !recarm_button ().is_active();
330 _rec_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
335 FP8Strip::set_select ()
337 if (!_select_plugin_functor.empty ()) {
338 assert (!_x_select_ctrl);
339 _select_plugin_functor ();
340 } else if (_x_select_ctrl) {
341 if (_x_select_ctrl->automation_state() == Touch && !_x_select_ctrl->touching ()) {
342 _x_select_ctrl->start_touch (_x_select_ctrl->session().transport_frame());
344 const bool on = !select_button ().is_active();
345 _x_select_ctrl->set_value (on ? 1.0 : 0.0, group_mode ());
349 /* *****************************************************************************
350 * Callbacks from Stripable, Update View
354 FP8Strip::notify_fader_changed ()
356 boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
362 val = ac->internal_to_interface (ac->get_value()) * 16368.f; /* 16 * 1023 */
363 val = std::max (0.f, std::min (1.f, val));
365 unsigned short mv = lrintf (val);
366 if (mv == _last_fader) {
370 _base.tx_midi3 (0xe0 + _id, (mv & 0x7f), (mv >> 7) & 0x7f);
374 FP8Strip::notify_solo_changed ()
377 _solo.set_active (_solo_ctrl->get_value () > 0);
379 _solo.set_active (false);
384 FP8Strip::notify_mute_changed ()
387 _mute.set_active (_mute_ctrl->get_value () > 0);
389 _mute.set_active (false);
394 FP8Strip::notify_rec_changed ()
397 recarm_button ().set_active (_rec_ctrl->get_value() > 0.);
399 recarm_button ().set_active (false);
404 FP8Strip::notify_pan_changed ()
410 FP8Strip::notify_x_select_changed ()
412 if (!_select_plugin_functor.empty ()) {
413 assert (!_x_select_ctrl);
417 if (_x_select_ctrl) {
418 assert (_select_plugin_functor.empty ());
419 select_button ().set_active (_x_select_ctrl->get_value() > 0.);
420 select_button ().set_color (0xffff00ff);
421 select_button ().set_blinking (false);
427 /* *****************************************************************************
428 * Periodic View Updates
432 FP8Strip::periodic_update_fader ()
434 boost::shared_ptr<AutomationControl> ac = _fader_ctrl;
435 if (!ac || _touching) {
439 ARDOUR::AutoState state = ac->automation_state();
440 if (state == Touch || state == Play) {
441 notify_fader_changed ();
446 FP8Strip::set_periodic_display_mode (DisplayMode m) {
448 if (_displaymode == SendDisplay || _displaymode == PluginParam) {
449 // need to change to 4 lines before calling set_text()
450 set_strip_mode (2); // 4 lines of small text
455 FP8Strip::periodic_update_meter ()
457 bool have_meter = false;
458 bool have_panner = false;
462 float dB = _peak_meter->meter_level (0, MeterMCP);
463 // TODO: deflect meter
464 int val = std::min (127.f, std::max (0.f, 2.f * dB + 127.f));
465 if (val != _last_meter || val > 0) {
466 _base.tx_midi2 (0xd0 + _id, val & 0x7f); // falls off automatically
471 if (0 != _last_meter) {
472 _base.tx_midi2 (0xd0 + _id, 0);
477 // show redux only if there's a meter, too (strip display mode 5)
478 if (_peak_meter && _redux_ctrl) {
479 float rx = (1.f - _redux_ctrl->get_parameter ()) * 127.f;
480 // TODO: deflect redux
481 int val = std::min (127.f, std::max (0.f, rx));
482 if (val != _last_redux) {
483 _base.tx_midi2 (0xd8 + _id, val & 0x7f);
487 if (0 != _last_redux) {
488 _base.tx_midi2 (0xd8 + _id, 0);
493 if (_displaymode == PluginParam) {
495 set_bar_mode (2); // Fill
496 set_text_line (0x01, value_as_string(_fader_ctrl->desc(), _fader_ctrl->get_value()));
497 float barpos = _fader_ctrl->internal_to_interface (_fader_ctrl->get_value());
498 int val = std::min (127.f, std::max (0.f, barpos * 128.f));
499 if (val != _last_barpos) {
500 _base.tx_midi3 (0xb0, 0x30 + _id, val & 0x7f);
504 set_bar_mode (4); // Off
505 set_text_line (0x01, "");
508 else if (_displaymode == SendDisplay) {
509 set_bar_mode (4); // Off
511 set_text_line (0x01, value_as_string(_fader_ctrl->desc(), _fader_ctrl->get_value()));
513 set_text_line (0x01, "");
515 } else if (_pan_ctrl) {
517 float panpos = _pan_ctrl->internal_to_interface (_pan_ctrl->get_value());
518 int val = std::min (127.f, std::max (0.f, panpos * 128.f));
519 set_bar_mode (1); // Bipolar
520 if (val != _last_barpos) {
521 _base.tx_midi3 (0xb0, 0x30 + _id, val & 0x7f);
524 set_text_line (0x01, _pan_ctrl->get_user_string ());
526 set_bar_mode (4); // Off
529 if (_displaymode == SendDisplay || _displaymode == PluginParam) {
530 set_strip_mode (2); // 4 lines of small text + value-bar
532 else if (have_meter && have_panner) {
533 set_strip_mode (5); // small meters + 3 lines of text (3rd is large) + value-bar
535 else if (have_meter) {
536 set_strip_mode (4); // big meters + 3 lines of text (3rd line is large)
538 else if (have_panner) {
539 set_strip_mode (0); // 3 lines of text (3rd line is large) + value-bar
541 set_strip_mode (0); // 3 lines of text (3rd line is large) + value-bar
546 FP8Strip::set_strip_mode (uint8_t strip_mode, bool clear)
548 if (strip_mode == _strip_mode && !clear) {
551 _strip_mode = strip_mode;
552 _base.tx_sysex (3, 0x13, _id, (_strip_mode & 0x07) | (clear ? 0x10 : 0));
553 //_base.tx_midi3 (0xb0, 0x38 + _id, _bar_mode);
557 FP8Strip::set_bar_mode (uint8_t bar_mode)
559 if (bar_mode == _bar_mode) {
562 _bar_mode = bar_mode;
563 _base.tx_midi3 (0xb0, 0x38 + _id, bar_mode);
567 FP8Strip::set_text_line (uint8_t line, std::string const& txt, bool inv)
570 if (_last_line[line] == txt) {
573 _base.tx_text (_id, line, inv ? 0x04 : 0x00, txt);
574 _last_line[line] = txt;
578 FP8Strip::periodic_update_timecode ()
580 if (_id >= 2 && _id < 6) {
581 std::string const& tc = _base.timecode();
584 if (tc.size () == 12) {
585 t = tc.substr (1 + (_id - 2) * 3, 2);
587 set_text_line (0x02, t);
592 FP8Strip::periodic ()
594 periodic_update_fader ();
595 periodic_update_meter ();
596 if (_displaymode != PluginSelect) {
597 periodic_update_timecode ();