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