b09fa32355974d6d1cfff939e8da5e2131e537dc
[ardour.git] / libs / surfaces / faderport8 / actions.cc
1 /* Faderport 8 Control Surface
2  * This is the button "Controller" of the MVC surface inteface,
3  * see callbacks.cc for the "View".
4  *
5  * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20  */
21
22 #include "ardour/dB.h"
23 #include "ardour/plugin_insert.h"
24 #include "ardour/session.h"
25 #include "ardour/session_configuration.h"
26 #include "ardour/types.h"
27
28 #include "gtkmm2ext/actions.h"
29
30 #include "faderport8.h"
31
32 #include "pbd/i18n.h"
33
34 using namespace std;
35 using namespace ARDOUR;
36 using namespace ArdourSurface::FP_NAMESPACE;
37 using namespace ArdourSurface::FP_NAMESPACE::FP8Types;
38
39 #define BindMethod(ID, CB) \
40         _ctrls.button (FP8Controls::ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this));
41
42 #define BindFunction(ID, ACT, CB, ...) \
43         _ctrls.button (FP8Controls::ID). ACT .connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this, __VA_ARGS__));
44
45 #define BindAction(ID, GRP, ITEM) \
46         _ctrls.button (FP8Controls::ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_action, this, GRP, ITEM));
47
48 #define BindUserAction(ID) \
49         _ctrls.button (ID).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_user, this, true, ID)); \
50 _ctrls.button (ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_user, this, false, ID));
51
52
53 /* Bind button signals (press, release) to callback methods
54  * (called once after constructing buttons).
55  * Bound actions are handled the the ctrl-surface thread.
56  */
57 void
58 FaderPort8::setup_actions ()
59 {
60         BindMethod (BtnPlay, button_play);
61         BindMethod (BtnStop, button_stop);
62         BindMethod (BtnLoop, button_loop);
63         BindMethod (BtnRecord, button_record);
64         BindMethod (BtnClick, button_metronom);
65         BindAction (BtnRedo, "Editor", "redo");
66
67         BindAction (BtnSave, "Common", "Save");
68         BindAction (BtnUndo, "Editor", "undo");
69         BindAction (BtnRedo, "Editor", "redo");
70
71 #ifdef FP8_MUTESOLO_UNDO
72         BindMethod (BtnSoloClear, button_solo_clear);
73 #else
74         BindAction (BtnSoloClear, "Main", "cancel-solo");
75 #endif
76         BindMethod (BtnMuteClear, button_mute_clear);
77
78         BindMethod (FP8Controls::BtnArmAll, button_arm_all);
79
80         BindFunction (BtnRewind, pressed, button_varispeed, false);
81         BindFunction (BtnFastForward, pressed, button_varispeed, true);
82
83         BindFunction (BtnPrev, released, button_prev_next, false);
84         BindFunction (BtnNext, released, button_prev_next, true);
85
86         BindFunction (BtnArm, pressed, button_arm, true);
87         BindFunction (BtnArm, released, button_arm, false);
88
89         BindFunction (BtnAOff, released, button_automation, ARDOUR::Off);
90         BindFunction (BtnATouch, released, button_automation, ARDOUR::Touch);
91         BindFunction (BtnARead, released, button_automation, ARDOUR::Play);
92         BindFunction (BtnAWrite, released, button_automation, ARDOUR::Write);
93         BindFunction (BtnALatch, released, button_automation, ARDOUR::Latch);
94
95         _ctrls.button (FP8Controls::BtnEncoder).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_encoder, this));
96         _ctrls.button (FP8Controls::BtnParam).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_parameter, this));
97
98
99         BindMethod (BtnBypass, button_bypass);
100         BindAction (BtnBypassAll, "Mixer", "ab-plugins");
101
102         BindAction (BtnMacro, "Common", "toggle-editor-and-mixer");
103         BindMethod (BtnOpen, button_open);
104
105         BindMethod (BtnLink, button_link);
106         BindMethod (BtnLock, button_lock);
107
108         // user-specific
109         for (FP8Controls::UserButtonMap::const_iterator i = _ctrls.user_buttons ().begin ();
110                         i != _ctrls.user_buttons ().end (); ++i) {
111                 BindUserAction ((*i).first);
112         }
113 }
114
115 /* ****************************************************************************
116  * Direct control callback Actions
117  */
118
119 void
120 FaderPort8::button_play ()
121 {
122         if (session->transport_rolling ()) {
123                 if (session->transport_speed () != 1.0) {
124                         session->request_transport_speed (1.0);
125                 } else {
126                         transport_stop ();
127                 }
128         } else {
129                 transport_play ();
130         }
131 }
132
133 void
134 FaderPort8::button_stop ()
135 {
136         if (session->transport_rolling ()) {
137                 transport_stop ();
138         } else {
139                 AccessAction ("Transport", "GotoStart");
140         }
141 }
142
143 void
144 FaderPort8::button_record ()
145 {
146         set_record_enable (!get_record_enabled ());
147 }
148
149 void
150 FaderPort8::button_loop ()
151 {
152         loop_toggle ();
153 }
154
155 void
156 FaderPort8::button_metronom ()
157 {
158         Config->set_clicking (!Config->get_clicking ());
159 }
160
161 void
162 FaderPort8::button_bypass ()
163 {
164         boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock();
165         if (pi) {
166                 pi->enable (! pi->enabled ());
167         } else {
168                 AccessAction ("Mixer", "ab-plugins");
169         }
170 }
171
172 void
173 FaderPort8::button_open ()
174 {
175         boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock();
176         if (pi) {
177                 pi->ToggleUI (); /* EMIT SIGNAL */
178         } else {
179                 AccessAction ("Common", "addExistingAudioFiles");
180         }
181 }
182 void
183 FaderPort8::button_lock ()
184 {
185         if (!_link_enabled) {
186                 AccessAction ("Editor", "lock");
187                 return;
188         }
189         if (_link_locked) {
190                 unlock_link ();
191         } else if (!_link_control.expired ()) {
192                 lock_link ();
193         }
194 }
195
196 void
197 FaderPort8::button_link ()
198 {
199         switch (_ctrls.fader_mode()) {
200                 case ModeTrack:
201                 case ModePan:
202                         if (_link_enabled) {
203                                 stop_link ();
204                         } else {
205                                 start_link ();
206                         }
207                         break;
208                 default:
209                         //AccessAction ("Window", "show-mixer");
210                         break;
211         }
212 }
213
214 void
215 FaderPort8::button_automation (ARDOUR::AutoState as)
216 {
217         FaderMode fadermode = _ctrls.fader_mode ();
218         switch (fadermode) {
219                 case ModePlugins:
220 #if 0 // Plugin Control Automation Mode
221                         for ( std::list <ProcessorCtrl>::iterator i = _proc_params.begin(); i != _proc_params.end(); ++i) {
222                                 ((*i).ac)->set_automation_state (as);
223                         }
224 #endif
225                         return;
226                 case ModeSend:
227                         if (first_selected_stripable()) {
228 #if 0 // Send Level Automation
229                                 boost::shared_ptr<Stripable> s = first_selected_stripable();
230                                 boost::shared_ptr<AutomationControl> send;
231                                 uint32_t i = 0;
232                                 while (0 != (send = s->send_level_controllable (i))) {
233                                         send->set_automation_state (as);
234                                         ++i;
235                                 }
236 #endif
237                         }
238                         return;
239                 default:
240                         break;
241         }
242
243         // TODO link/lock control automation?
244
245         // apply to all selected tracks
246         StripableList all;
247         session->get_stripables (all);
248         for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
249                 if ((*i)->is_master() || (*i)->is_monitor()) {
250                         continue;
251                 }
252                 if (!(*i)->is_selected()) {
253                         continue;
254                 }
255                 boost::shared_ptr<AutomationControl> ac;
256                 switch (fadermode) {
257                         case ModeTrack:
258                                 ac = (*i)->gain_control ();
259                                 break;
260                         case ModePan:
261                                 ac = (*i)->pan_azimuth_control ();
262                                 break;
263                         default:
264                                 break;
265                 }
266                 if (ac) {
267                         ac->set_automation_state (as);
268                 }
269         }
270 }
271
272 void
273 FaderPort8::button_varispeed (bool ffw)
274 {
275         /* pressing both rew + ffwd -> return to zero */
276         FP8ButtonInterface& b_rew = _ctrls.button (FP8Controls::BtnRewind);
277         FP8ButtonInterface& b_ffw = _ctrls.button (FP8Controls::BtnFastForward);
278         if (b_rew.is_pressed () && b_ffw.is_pressed ()){
279                 // stop key-repeat
280                 dynamic_cast<FP8RepeatButton*>(&b_ffw)->stop_repeat();
281                 dynamic_cast<FP8RepeatButton*>(&b_rew)->stop_repeat();
282                 session->request_locate (0, false);
283                 return;
284         }
285
286         // switch play direction, if needed
287         if (ffw) {
288                 if (session->transport_speed () <= 0) {
289                         session->request_transport_speed (1.0);
290                         return ;
291                 }
292         } else {
293                 if (session->transport_speed () >= 0) {
294                         session->request_transport_speed (-1.0);
295                         return ;
296                 }
297         }
298         // incremetally increase speed. double speed every 10 clicks
299         // (keypress auto-repeat is 100ms)
300         float maxspeed = Config->get_shuttle_max_speed();
301         float speed = exp2f(0.1f) * session->transport_speed ();
302         speed = std::max (-maxspeed, std::min (maxspeed, speed));
303         session->request_transport_speed (speed, false);
304 }
305
306 #ifdef FP8_MUTESOLO_UNDO
307 void
308 FaderPort8::button_solo_clear ()
309 {
310         bool soloing = session->soloing() || session->listening();
311 #ifdef MIXBUS
312         soloing |= session->mixbus_soloed();
313 #endif
314         if (soloing) {
315                 StripableList all;
316                 session->get_stripables (all);
317                 for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
318                         if ((*i)->is_master() || (*i)->is_auditioner() || (*i)->is_monitor()) {
319                                 continue;
320                         }
321                         boost::shared_ptr<SoloControl> sc = (*i)->solo_control();
322                         if (sc && sc->self_soloed ()) {
323                                 _solo_state.push_back (boost::weak_ptr<AutomationControl>(sc));
324                         }
325                 }
326                 cancel_all_solo (); // AccessAction ("Main", "cancel-solo");
327         } else {
328                 /* restore solo */
329                 boost::shared_ptr<ControlList> cl (new ControlList);
330                 for (std::vector <boost::weak_ptr<AutomationControl> >::const_iterator i = _solo_state.begin(); i != _solo_state.end(); ++i) {
331                         boost::shared_ptr<AutomationControl> ac = (*i).lock();
332                         if (!ac) {
333                                 continue;
334                         }
335                         ac->start_touch (ac->session().transport_sample());
336                         cl->push_back (ac);
337                 }
338                 if (!cl->empty()) {
339                         session->set_controls (cl, 1.0, PBD::Controllable::NoGroup);
340                 }
341         }
342 }
343 #endif
344
345 void
346 FaderPort8::button_mute_clear ()
347 {
348 #ifdef FP8_MUTESOLO_UNDO
349         if (session->muted ()) {
350                 _mute_state = session->cancel_all_mute ();
351         } else {
352                 /* restore mute */
353                 boost::shared_ptr<ControlList> cl (new ControlList);
354                 for (std::vector <boost::weak_ptr<AutomationControl> >::const_iterator i = _mute_state.begin(); i != _mute_state.end(); ++i) {
355                         boost::shared_ptr<AutomationControl> ac = (*i).lock();
356                         if (!ac) {
357                                 continue;
358                         }
359                         cl->push_back (ac);
360                         ac->start_touch (ac->session().transport_sample());
361                 }
362                 if (!cl->empty()) {
363                         session->set_controls (cl, 1.0, PBD::Controllable::NoGroup);
364                 }
365         }
366 #else
367         session->cancel_all_mute ();
368 #endif
369 }
370
371 void
372 FaderPort8::button_arm_all ()
373 {
374         BasicUI::all_tracks_rec_in ();
375 }
376
377 /* access generic action */
378 void
379 FaderPort8::button_action (const std::string& group, const std::string& item)
380 {
381         AccessAction (group, item);
382 }
383
384 /* ****************************************************************************
385  * Control Interaction (encoder)
386  */
387
388 void
389 FaderPort8::handle_encoder_pan (int steps)
390 {
391         boost::shared_ptr<Stripable> s = first_selected_stripable();
392         if (s) {
393                 boost::shared_ptr<AutomationControl> ac;
394                 if (shift_mod () || _ctrls.fader_mode() == ModePan) {
395                         ac = s->pan_width_control ();
396                 } else {
397                         ac = s->pan_azimuth_control ();
398                 }
399                 if (ac) {
400                         ac->start_touch (ac->session().transport_sample());
401                         if (steps == 0) {
402                                 ac->set_value (ac->normal(), PBD::Controllable::UseGroup);
403                         } else {
404                                 double v = ac->internal_to_interface (ac->get_value());
405                                 v = std::max (0.0, std::min (1.0, v + steps * .01));
406                                 ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
407                         }
408                 }
409         }
410 }
411
412 void
413 FaderPort8::handle_encoder_link (int steps)
414 {
415         if (_link_control.expired ()) {
416                 return;
417         }
418         boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (_link_control.lock ());
419         if (!ac) {
420                 return;
421         }
422
423         double v = ac->internal_to_interface (ac->get_value());
424         ac->start_touch (ac->session().transport_sample());
425
426         if (steps == 0) {
427                 ac->set_value (ac->normal(), PBD::Controllable::UseGroup);
428                 return;
429         }
430
431         if (ac->desc().toggled) {
432                 v = v > 0 ? 0. : 1.;
433         } else if (ac->desc().integer_step) {
434                 v += steps / (1.f + ac->desc().upper - ac->desc().lower);
435         } else if (ac->desc().enumeration) {
436                 ac->set_value (ac->desc().step_enum (ac->get_value(), steps < 0), PBD::Controllable::UseGroup);
437                 return;
438         } else {
439                 v = std::max (0.0, std::min (1.0, v + steps * .01));
440         }
441         ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
442 }
443
444
445 /* ****************************************************************************
446  * Mode specific and internal callbacks
447  */
448
449 /* handle "ARM" press -- act like shift, change "Select" button mode */
450 void
451 FaderPort8::button_arm (bool press)
452 {
453         FaderMode fadermode = _ctrls.fader_mode ();
454         if (fadermode == ModeTrack || fadermode == ModePan) {
455                 _ctrls.button (FP8Controls::BtnArm).set_active (press);
456                 ARMButtonChange (press); /* EMIT SIGNAL */
457         }
458 }
459
460 void
461 FaderPort8::button_prev_next (bool next)
462 {
463         switch (_ctrls.nav_mode()) {
464                 case NavChannel:
465                         select_prev_next (next);
466                         break;
467                 case NavMaster:
468                 case NavScroll:
469                         bank (!next, false);
470                         break;
471                 case NavBank:
472                         bank (!next, true);
473                         break;
474                 case NavZoom:
475                         if (next) {
476                                 VerticalZoomInSelected ();
477                         } else {
478                                 VerticalZoomOutSelected ();
479                         }
480                         break;
481                 case NavSection:
482                         if (next) {
483                                 AccessAction ("Region", "nudge-forward");
484                         } else {
485                                 AccessAction ("Region", "nudge-backward");
486                         }
487                         break;
488                 case NavMarker:
489                         if (next) {
490                                 next_marker ();
491                         } else {
492                                 prev_marker ();
493                         }
494                         break;
495         }
496 }
497
498 /* handle navigation encoder press */
499 void
500 FaderPort8::button_encoder ()
501 {
502         /* special-case metronome level */
503         if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
504                 Config->set_click_gain (1.0);
505                 _ctrls.button (FP8Controls::BtnClick).ignore_release();
506                 return;
507         }
508         switch (_ctrls.nav_mode()) {
509                 case NavZoom:
510                         ZoomToSession (); // XXX undo zoom
511                         break;
512                 case NavScroll:
513                         ZoomToSession ();
514                         break;
515                 case NavChannel:
516                         AccessAction ("Editor", "select-topmost");
517                         break;
518                 case NavBank:
519                         move_selected_into_view ();
520                         break;
521                 case NavMaster:
522                         {
523                                 /* master || monitor level -- reset to 0dB */
524                                 boost::shared_ptr<AutomationControl> ac;
525                                 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
526                                         ac = session->monitor_out()->gain_control ();
527                                 } else if (session->master_out()) {
528                                         ac = session->master_out()->gain_control ();
529                                 }
530                                 if (ac) {
531                                         ac->start_touch (ac->session().transport_sample());
532                                         ac->set_value (ac->normal(), PBD::Controllable::NoGroup);
533                                 }
534                         }
535                         break;
536                 case NavSection:
537                         // TODO nudge
538                         break;
539                 case NavMarker:
540                         {
541                                 string markername;
542                                 /* Don't add another mark if one exists within 1/100th of a second of
543                                  * the current position and we're not rolling.
544                                  */
545                                 samplepos_t where = session->audible_sample();
546                                 if (session->transport_stopped() && session->locations()->mark_at (where, session->sample_rate() / 100.0)) {
547                                         return;
548                                 }
549
550                                 session->locations()->next_available_name (markername,"mark");
551                                 add_marker (markername);
552                         }
553                         break;
554         }
555 }
556
557 /* handle navigation encoder turn */
558 void
559 FaderPort8::encoder_navigate (bool neg, int steps)
560 {
561         /* special-case metronome level */
562         if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
563                 // compare to ARDOUR_UI::click_button_scroll()
564                 gain_t gain = Config->get_click_gain();
565                 float gain_db = accurate_coefficient_to_dB (gain);
566                 gain_db += (neg ? -1.f : 1.f) * steps;
567                 gain_db = std::max (-60.f, gain_db);
568                 gain = dB_to_coefficient (gain_db);
569                 gain = std::min (gain, Config->get_max_gain());
570                 Config->set_click_gain (gain);
571                 _ctrls.button (FP8Controls::BtnClick).ignore_release();
572                 return;
573         }
574
575         switch (_ctrls.nav_mode()) {
576                 case NavChannel:
577                         if (neg) {
578                                 AccessAction ("Mixer", "scroll-left");
579                                 AccessAction ("Editor", "step-tracks-up");
580                         } else {
581                                 AccessAction ("Mixer", "scroll-right");
582                                 AccessAction ("Editor", "step-tracks-down");
583                         }
584                         break;
585                 case NavZoom:
586                         if (neg) {
587                                 ZoomOut ();
588                         } else {
589                                 ZoomIn ();
590                         }
591                         break;
592                 case NavMarker:
593                 case NavScroll:
594                         ScrollTimeline ((neg ? -1.f : 1.f) * steps / (shift_mod() ? 1024.f : 256.f));
595                         break;
596                 case NavBank:
597                         bank (neg, false);
598                         break;
599                 case NavMaster:
600                         {
601                                 /* master || monitor level */
602                                 boost::shared_ptr<AutomationControl> ac;
603                                 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
604                                         ac = session->monitor_out()->gain_control ();
605                                 } else if (session->master_out()) {
606                                         ac = session->master_out()->gain_control ();
607                                 }
608                                 if (ac) {
609                                         double v = ac->internal_to_interface (ac->get_value());
610                                         v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
611                                         ac->start_touch (ac->session().transport_sample());
612                                         ac->set_value (ac->interface_to_internal(v), PBD::Controllable::NoGroup);
613                                 }
614                         }
615                         break;
616                 case NavSection:
617                         if (neg) {
618                                 AccessAction ("Common", "nudge-playhead-backward");
619                         } else {
620                                 AccessAction ("Common", "nudge-playhead-forward");
621                         }
622                         break;
623         }
624 }
625
626 /* handle pan/param encoder press */
627 void
628 FaderPort8::button_parameter ()
629 {
630         switch (_ctrls.fader_mode()) {
631                 case ModeTrack:
632                 case ModePan:
633                         if (_link_enabled || _link_locked) {
634                                 handle_encoder_link (0);
635                         } else {
636                                 handle_encoder_pan (0);
637                         }
638                         break;
639                 case ModePlugins:
640                         toggle_preset_param_mode ();
641                         break;
642                 case ModeSend:
643                         break;
644         }
645 }
646
647 /* handle pan/param encoder turn */
648 void
649 FaderPort8::encoder_parameter (bool neg, int steps)
650 {
651         switch (_ctrls.fader_mode()) {
652                 case ModeTrack:
653                 case ModePan:
654                         if (steps != 0) {
655                                 if (_link_enabled || _link_locked) {
656                                         handle_encoder_link (neg ? -steps : steps);
657                                 } else {
658                                         handle_encoder_pan (neg ? -steps : steps);
659                                 }
660                         }
661                         break;
662                 case ModePlugins:
663                 case ModeSend:
664                         while (steps > 0) {
665                                 bank_param (neg, shift_mod());
666                                 --steps;
667                         }
668                         break;
669         }
670 }
671
672 /* handle user-specific actions */
673 void
674 FaderPort8::button_user (bool press, FP8Controls::ButtonId btn)
675 {
676         _user_action_map[btn].call (*this, press);
677 }