Faderport8 control surface support
[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         transport_stop ();
122 }
123
124 void
125 FaderPort8::button_record ()
126 {
127         set_record_enable (!get_record_enabled ());
128 }
129
130 void
131 FaderPort8::button_loop ()
132 {
133         loop_toggle ();
134 }
135
136 void
137 FaderPort8::button_metronom ()
138 {
139         Config->set_clicking (!Config->get_clicking ());
140 }
141
142 void
143 FaderPort8::button_automation (ARDOUR::AutoState as)
144 {
145         FaderMode fadermode = _ctrls.fader_mode ();
146         switch (fadermode) {
147                 case ModePlugins:
148 #if 0 // Plugin Control Automation Mode
149                         for ( std::list <ProcessorCtrl>::iterator i = _proc_params.begin(); i != _proc_params.end(); ++i) {
150                                 ((*i).ac)->set_automation_state (as);
151                         }
152 #endif
153                         return;
154                 case ModeSend:
155                         if (first_selected_stripable()) {
156 #if 0 // Send Level Automation
157                                 boost::shared_ptr<Stripable> s = first_selected_stripable();
158                                 boost::shared_ptr<AutomationControl> send;
159                                 uint32_t i = 0;
160                                 while (0 != (send = s->send_level_controllable (i))) {
161                                         send->set_automation_state (as);
162                                         ++i;
163                                 }
164 #endif
165                         }
166                         return;
167                 default:
168                         break;
169         }
170
171         // apply to all selected tracks
172         StripableList all;
173         session->get_stripables (all);
174         for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
175                 if ((*i)->is_master() || (*i)->is_monitor()) {
176                         continue;
177                 }
178                 if (!(*i)->is_selected()) {
179                         continue;
180                 }
181                 boost::shared_ptr<AutomationControl> ac;
182                 switch (fadermode) {
183                         case ModeTrack:
184                                 ac = (*i)->gain_control ();
185                                 break;
186                         case ModePan:
187                                 ac = (*i)->pan_azimuth_control ();
188                                 break;
189                         default:
190                                 break;
191                 }
192                 if (ac) {
193                         ac->set_automation_state (as);
194                 }
195         }
196 }
197
198 void
199 FaderPort8::button_varispeed (bool ffw)
200 {
201         /* pressing both rew + ffwd -> return to zero */
202         FP8ButtonInterface& b_rew = _ctrls.button (FP8Controls::BtnRewind);
203         FP8ButtonInterface& b_ffw = _ctrls.button (FP8Controls::BtnFastForward);
204         if (b_rew.is_pressed () && b_ffw.is_pressed ()){
205                 // stop key-repeat
206                 dynamic_cast<FP8RepeatButton*>(&b_ffw)->stop_repeat();
207                 dynamic_cast<FP8RepeatButton*>(&b_rew)->stop_repeat();
208                 AccessAction ("Transport", "GotoStart");
209                 return;
210         }
211
212         // switch play direction, if needed
213         if (ffw) {
214                 if (session->transport_speed () <= 0) {
215                         session->request_transport_speed (1.0);
216                         return ;
217                 }
218         } else {
219                 if (session->transport_speed () >= 0) {
220                         session->request_transport_speed (-1.0);
221                         return ;
222                 }
223         }
224         // incremetally increase speed. double speed every 10 clicks
225         // (keypress auto-repeat is 100ms)
226         float maxspeed = Config->get_shuttle_max_speed();
227         float speed = exp2f(0.1f) * session->transport_speed ();
228         speed = std::max (-maxspeed, std::min (maxspeed, speed));
229         session->request_transport_speed (speed, false);
230 }
231
232 void
233 FaderPort8::button_mute_clear ()
234 {
235         StripableList all;
236         session->get_stripables (all);
237         boost::shared_ptr<ControlList> cl (new ControlList);
238         for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
239                 if ((*i)->is_master() || (*i)->is_monitor()) {
240                         continue;
241                 }
242                 boost::shared_ptr<AutomationControl> ac = (*i)->mute_control();
243                 if (ac) {
244                         cl->push_back (ac);
245                 }
246         }
247         session->set_controls (cl, 0.0, PBD::Controllable::UseGroup);
248 }
249
250 void
251 FaderPort8::button_arm (bool press)
252 {
253         FaderMode fadermode = _ctrls.fader_mode ();
254         if (fadermode == ModeTrack || fadermode == ModePan) {
255                 _ctrls.button (FP8Controls::BtnArm).set_active (press);
256                 ARMButtonChange (press);
257         }
258 }
259
260 void
261 FaderPort8::button_arm_all ()
262 {
263         BasicUI::all_tracks_rec_in ();
264 }
265
266 void
267 FaderPort8::button_action (const std::string& group, const std::string& item)
268 {
269         AccessAction (group, item);
270 }
271
272 void
273 FaderPort8::button_prev_next (bool next)
274 {
275         switch (_ctrls.nav_mode()) {
276                 case NavMaster:
277                 case NavChannel:
278                 case NavScroll:
279                         bank (!next, false);
280                         break;
281                 case NavBank:
282                         bank (!next, true);
283                         break;
284                 case NavZoom:
285                         if (next) {
286                                 StepTracksDown ();
287                         } else {
288                                 StepTracksUp ();
289                         }
290                         break;
291                 case NavSection:
292                         // TODO nudge
293                         break;
294                 case NavMarker:
295                         if (next) {
296                                 next_marker ();
297                         } else {
298                                 prev_marker ();
299                         }
300                         break;
301         }
302 }
303
304 /* handle navigation encoder press */
305 void
306 FaderPort8::button_encoder ()
307 {
308         switch (_ctrls.nav_mode()) {
309                 case NavZoom:
310                         ZoomToSession (); // XXX undo zoom
311                         break;
312                 case NavScroll:
313                         ZoomToSession ();
314                         break;
315                 case NavChannel:
316                 case NavBank:
317                         move_selected_into_view ();
318                         break;
319                 case NavMaster:
320                         {
321                                 /* master || monitor level -- reset to 0dB */
322                                 boost::shared_ptr<AutomationControl> ac;
323                                 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
324                                         ac = session->monitor_out()->gain_control ();
325                                 } else if (session->master_out()) {
326                                         ac = session->master_out()->gain_control ();
327                                 }
328                                 if (ac) {
329                                         ac->set_value (ac->normal(), PBD::Controllable::NoGroup);
330                                 }
331                         }
332                         break;
333                 case NavSection:
334                         break;
335                 case NavMarker:
336                         {
337                                 string markername;
338                                 /* Don't add another mark if one exists within 1/100th of a second of
339                                  * the current position and we're not rolling.
340                                  */
341                                 framepos_t where = session->audible_frame();
342                                 if (session->transport_stopped() && session->locations()->mark_at (where, session->frame_rate() / 100.0)) {
343                                         return;
344                                 }
345
346                                 session->locations()->next_available_name (markername,"mark");
347                                 add_marker (markername);
348                         }
349                         break;
350         }
351 }
352
353 /* handle navigation encoder turn */
354 void
355 FaderPort8::encoder_navigate (bool neg, int steps)
356 {
357         /* special-case metronome level */
358         if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
359                 // compare to ARDOUR_UI::click_button_scroll()
360                 gain_t gain = Config->get_click_gain();
361                 float gain_db = accurate_coefficient_to_dB (gain);
362                 gain_db += (neg ? -1.f : 1.f) * steps;
363                 gain_db = std::max (-60.f, gain_db);
364                 gain = dB_to_coefficient (gain_db);
365                 gain = std::min (gain, Config->get_max_gain());
366                 Config->set_click_gain (gain);
367                 _ctrls.button (FP8Controls::BtnClick).ignore_release();
368                 return;
369         }
370
371         switch (_ctrls.nav_mode()) {
372                 case NavChannel:
373                         if (neg) {
374                                 StepTracksUp ();
375                         } else {
376                                 StepTracksDown ();
377                         }
378                         break;
379                 case NavZoom:
380                         if (neg) {
381                                 ZoomOut ();
382                         } else {
383                                 ZoomIn ();
384                         }
385                         break;
386                 case NavMarker:
387                 case NavScroll:
388                         ScrollTimeline ((neg ? -1.f : 1.f) * steps / (shift_mod() ? 1024.f : 256.f));
389                         break;
390                 case NavBank:
391                         bank (neg, false);
392                         break;
393                 case NavMaster:
394                         {
395                                 /* master || monitor level */
396                                 boost::shared_ptr<AutomationControl> ac;
397                                 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
398                                         ac = session->monitor_out()->gain_control ();
399                                 } else if (session->master_out()) {
400                                         ac = session->master_out()->gain_control ();
401                                 }
402                                 if (ac) {
403                                         double v = ac->internal_to_interface (ac->get_value());
404                                         v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
405                                         ac->set_value (ac->interface_to_internal(v), PBD::Controllable::NoGroup);
406                                 }
407                         }
408                         break;
409                 case NavSection:
410                         // nudge event
411                         break;
412         }
413 }
414
415 /* handle pan/param encoder press */
416 void
417 FaderPort8::button_parameter ()
418 {
419         switch (_ctrls.fader_mode()) {
420                 case ModeTrack:
421                 case ModePan:
422                         // pan-width see FaderPort8::encoder_parameter()
423                         break;
424                 case ModePlugins:
425                         break;
426                 case ModeSend:
427                         break;
428         }
429 }
430
431 /* handle pan/param encoder turn */
432 void
433 FaderPort8::encoder_parameter (bool neg, int steps)
434 {
435         switch (_ctrls.fader_mode()) {
436                 case ModeTrack:
437                 case ModePan:
438                         {
439                                 boost::shared_ptr<Stripable> s = first_selected_stripable();
440                                 if (s) {
441                                         boost::shared_ptr<AutomationControl> ac;
442                                         if (_ctrls.button (FP8Controls::BtnParam).is_pressed ()) {
443                                                 ac = s->pan_width_control ();
444                                         } else {
445                                                 ac = s->pan_azimuth_control ();
446                                         }
447                                         if (ac) {
448                                                 double v = ac->internal_to_interface (ac->get_value());
449                                                 v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
450                                                 ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
451                                         }
452                                 }
453                         }
454                         break;
455                 case ModePlugins:
456                 case ModeSend:
457                         while (steps > 0) {
458                                 bank_param (neg, false);
459                                 --steps;
460                         }
461                         break;
462         }
463 }
464
465 /* handle user-specific actions */
466 void
467 FaderPort8::button_user (bool press, FP8Controls::ButtonId btn)
468 {
469         _user_action_map[btn].call (*this, press);
470 }