OSC: use internal_to_interface or reverse
[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 #ifdef FP8_MUTESOLO_UNDO
291         if (session->muted ()) {
292                 _mute_state = session->cancel_all_mute ();
293         } else {
294                 /* restore mute */
295                 boost::shared_ptr<ControlList> cl (new ControlList);
296                 for (std::vector <boost::weak_ptr<AutomationControl> >::const_iterator i = _mute_state.begin(); i != _mute_state.end(); ++i) {
297                         boost::shared_ptr<AutomationControl> ac = (*i).lock();
298                         if (!ac) {
299                                 continue;
300                         }
301                         cl->push_back (ac);
302                         if (ac->automation_state() == Touch && !ac->touching ()) {
303                                 ac->start_touch (ac->session().transport_frame());
304                         }
305                 }
306                 if (!cl->empty()) {
307                         session->set_controls (cl, 1.0, PBD::Controllable::NoGroup);
308                 }
309         }
310 #else
311         session->cancel_all_mute ();
312 #endif
313 }
314
315 void
316 FaderPort8::button_arm_all ()
317 {
318         BasicUI::all_tracks_rec_in ();
319 }
320
321 /* access generic action */
322 void
323 FaderPort8::button_action (const std::string& group, const std::string& item)
324 {
325         AccessAction (group, item);
326 }
327
328 /* ****************************************************************************
329  * Mode specific and internal callbacks
330  */
331
332 /* handle "ARM" press -- act like shift, change "Select" button mode */
333 void
334 FaderPort8::button_arm (bool press)
335 {
336         FaderMode fadermode = _ctrls.fader_mode ();
337         if (fadermode == ModeTrack || fadermode == ModePan) {
338                 _ctrls.button (FP8Controls::BtnArm).set_active (press);
339                 ARMButtonChange (press); /* EMIT SIGNAL */
340         }
341 }
342
343 void
344 FaderPort8::button_prev_next (bool next)
345 {
346         switch (_ctrls.nav_mode()) {
347                 case NavChannel:
348                         select_prev_next (next);
349                         break;
350                 case NavMaster:
351                 case NavScroll:
352                         bank (!next, false);
353                         break;
354                 case NavBank:
355                         bank (!next, true);
356                         break;
357                 case NavZoom:
358                         if (next) {
359                                 VerticalZoomInSelected ();
360                         } else {
361                                 VerticalZoomOutSelected ();
362                         }
363                         break;
364                 case NavSection:
365                         if (next) {
366                                 AccessAction ("Region", "nudge-forward");
367                         } else {
368                                 AccessAction ("Region", "nudge-backward");
369                         }
370                         break;
371                 case NavMarker:
372                         if (next) {
373                                 next_marker ();
374                         } else {
375                                 prev_marker ();
376                         }
377                         break;
378         }
379 }
380
381 /* handle navigation encoder press */
382 void
383 FaderPort8::button_encoder ()
384 {
385         /* special-case metronome level */
386         if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
387                 Config->set_click_gain (1.0);
388                 _ctrls.button (FP8Controls::BtnClick).ignore_release();
389                 return;
390         }
391         switch (_ctrls.nav_mode()) {
392                 case NavZoom:
393                         ZoomToSession (); // XXX undo zoom
394                         break;
395                 case NavScroll:
396                         ZoomToSession ();
397                         break;
398                 case NavChannel:
399                         AccessAction ("Editor", "select-topmost");
400                         break;
401                 case NavBank:
402                         move_selected_into_view ();
403                         break;
404                 case NavMaster:
405                         {
406                                 /* master || monitor level -- reset to 0dB */
407                                 boost::shared_ptr<AutomationControl> ac;
408                                 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
409                                         ac = session->monitor_out()->gain_control ();
410                                 } else if (session->master_out()) {
411                                         ac = session->master_out()->gain_control ();
412                                 }
413                                 if (ac) {
414                                         if (ac->automation_state() == Touch && !ac->touching ()) {
415                                                 ac->start_touch (ac->session().transport_frame());
416                                         }
417                                         ac->set_value (ac->normal(), PBD::Controllable::NoGroup);
418                                 }
419                         }
420                         break;
421                 case NavSection:
422                         // TODO nudge
423                         break;
424                 case NavMarker:
425                         {
426                                 string markername;
427                                 /* Don't add another mark if one exists within 1/100th of a second of
428                                  * the current position and we're not rolling.
429                                  */
430                                 framepos_t where = session->audible_frame();
431                                 if (session->transport_stopped() && session->locations()->mark_at (where, session->frame_rate() / 100.0)) {
432                                         return;
433                                 }
434
435                                 session->locations()->next_available_name (markername,"mark");
436                                 add_marker (markername);
437                         }
438                         break;
439         }
440 }
441
442 /* handle navigation encoder turn */
443 void
444 FaderPort8::encoder_navigate (bool neg, int steps)
445 {
446         /* special-case metronome level */
447         if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
448                 // compare to ARDOUR_UI::click_button_scroll()
449                 gain_t gain = Config->get_click_gain();
450                 float gain_db = accurate_coefficient_to_dB (gain);
451                 gain_db += (neg ? -1.f : 1.f) * steps;
452                 gain_db = std::max (-60.f, gain_db);
453                 gain = dB_to_coefficient (gain_db);
454                 gain = std::min (gain, Config->get_max_gain());
455                 Config->set_click_gain (gain);
456                 _ctrls.button (FP8Controls::BtnClick).ignore_release();
457                 return;
458         }
459
460         switch (_ctrls.nav_mode()) {
461                 case NavChannel:
462                         if (neg) {
463                                 AccessAction ("Mixer", "scroll-left");
464                                 AccessAction ("Editor", "step-tracks-up");
465                         } else {
466                                 AccessAction ("Mixer", "scroll-right");
467                                 AccessAction ("Editor", "step-tracks-down");
468                         }
469                         break;
470                 case NavZoom:
471                         if (neg) {
472                                 ZoomOut ();
473                         } else {
474                                 ZoomIn ();
475                         }
476                         break;
477                 case NavMarker:
478                 case NavScroll:
479                         ScrollTimeline ((neg ? -1.f : 1.f) * steps / (shift_mod() ? 1024.f : 256.f));
480                         break;
481                 case NavBank:
482                         bank (neg, false);
483                         break;
484                 case NavMaster:
485                         {
486                                 /* master || monitor level */
487                                 boost::shared_ptr<AutomationControl> ac;
488                                 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
489                                         ac = session->monitor_out()->gain_control ();
490                                 } else if (session->master_out()) {
491                                         ac = session->master_out()->gain_control ();
492                                 }
493                                 if (ac) {
494                                         double v = ac->internal_to_interface (ac->get_value());
495                                         v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
496                                         if (ac->automation_state() == Touch && !ac->touching ()) {
497                                                 ac->start_touch (ac->session().transport_frame());
498                                         }
499                                         ac->set_value (ac->interface_to_internal(v), PBD::Controllable::NoGroup);
500                                 }
501                         }
502                         break;
503                 case NavSection:
504                         if (neg) {
505                                 AccessAction ("Common", "nudge-playhead-backward");
506                         } else {
507                                 AccessAction ("Common", "nudge-playhead-forward");
508                         }
509                         break;
510         }
511 }
512
513 /* handle pan/param encoder press */
514 void
515 FaderPort8::button_parameter ()
516 {
517         switch (_ctrls.fader_mode()) {
518                 case ModeTrack:
519                 case ModePan:
520                         {
521                                 boost::shared_ptr<Stripable> s = first_selected_stripable();
522                                 if (s) {
523                                         boost::shared_ptr<AutomationControl> ac;
524                                         if (shift_mod () || _ctrls.fader_mode() == ModePan) {
525                                                 ac = s->pan_width_control ();
526                                         } else {
527                                                 ac = s->pan_azimuth_control ();
528                                         }
529                                         if (ac) {
530                                                 if (ac->automation_state() == Touch && !ac->touching ()) {
531                                                         ac->start_touch (ac->session().transport_frame());
532                                                 }
533                                                 ac->set_value (ac->normal(), PBD::Controllable::UseGroup);
534                                         }
535                                 }
536                         }
537                         break;
538                 case ModePlugins:
539                         break;
540                 case ModeSend:
541                         break;
542         }
543 }
544
545 /* handle pan/param encoder turn */
546 void
547 FaderPort8::encoder_parameter (bool neg, int steps)
548 {
549         switch (_ctrls.fader_mode()) {
550                 case ModeTrack:
551                 case ModePan:
552                         {
553                                 boost::shared_ptr<Stripable> s = first_selected_stripable();
554                                 if (s) {
555                                         boost::shared_ptr<AutomationControl> ac;
556                                         if (shift_mod () || _ctrls.fader_mode() == ModePan) {
557                                                 ac = s->pan_width_control ();
558                                         } else {
559                                                 ac = s->pan_azimuth_control ();
560                                         }
561                                         if (ac) {
562                                                 double v = ac->internal_to_interface (ac->get_value());
563                                                 v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
564                                                 if (ac->automation_state() == Touch && !ac->touching ()) {
565                                                         ac->start_touch (ac->session().transport_frame());
566                                                 }
567                                                 ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
568                                         }
569                                 }
570                         }
571                         break;
572                 case ModePlugins:
573                 case ModeSend:
574                         while (steps > 0) {
575                                 bank_param (neg, false);
576                                 --steps;
577                         }
578                         break;
579         }
580 }
581
582 /* handle user-specific actions */
583 void
584 FaderPort8::button_user (bool press, FP8Controls::ButtonId btn)
585 {
586         _user_action_map[btn].call (*this, press);
587 }