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