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