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