1 /* Faderport 8 Control Surface
2 * This is the button "Controller" of the MVC surface inteface,
3 * see callbacks.cc for the "View".
5 * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
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.
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.
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.
22 #include "ardour/dB.h"
23 #include "ardour/session.h"
24 #include "ardour/session_configuration.h"
25 #include "ardour/types.h"
27 #include "gtkmm2ext/actions.h"
29 #include "faderport8.h"
33 using namespace ARDOUR;
34 using namespace ArdourSurface;
36 using namespace ArdourSurface::FP8Types;
38 #define BindMethod(ID, CB) \
39 _ctrls.button (FP8Controls::ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this));
41 #define BindFunction(ID, ACT, CB, ...) \
42 _ctrls.button (FP8Controls::ID). ACT .connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this, __VA_ARGS__));
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));
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));
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.
57 FaderPort8::setup_actions ()
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");
66 BindAction (BtnSave, "Common", "Save");
67 BindAction (BtnUndo, "Editor", "undo");
68 BindAction (BtnRedo, "Editor", "redo");
70 #ifdef FP8_MUTESOLO_UNDO
71 BindMethod (BtnSoloClear, button_solo_clear);
73 BindAction (BtnSoloClear, "Main", "cancel-solo");
75 BindMethod (BtnMuteClear, button_mute_clear);
77 BindMethod (FP8Controls::BtnArmAll, button_arm_all);
79 BindFunction (BtnRewind, pressed, button_varispeed, false);
80 BindFunction (BtnFastForward, pressed, button_varispeed, true);
82 BindFunction (BtnPrev, released, button_prev_next, false);
83 BindFunction (BtnNext, released, button_prev_next, true);
85 BindFunction (BtnArm, pressed, button_arm, true);
86 BindFunction (BtnArm, released, button_arm, false);
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);
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));
97 BindAction (BtnBypass, "Mixer", "ab-plugins");
98 BindAction (BtnBypassAll, "Mixer", "ab-plugins"); // XXX
100 BindAction (BtnMacro, "Mixer", "show-editor");
101 BindAction (BtnLink, "Window", "show-mixer");
103 BindAction (BtnOpen, "Common", "addExistingAudioFiles");
104 BindAction (BtnLock, "Editor", "lock");
107 for (FP8Controls::UserButtonMap::const_iterator i = _ctrls.user_buttons ().begin ();
108 i != _ctrls.user_buttons ().end (); ++i) {
109 BindUserAction ((*i).first);
113 /* ****************************************************************************
114 * Direct control callback Actions
118 FaderPort8::button_play ()
120 if (session->transport_rolling ()) {
121 if (session->transport_speed () != 1.0) {
122 session->request_transport_speed (1.0);
132 FaderPort8::button_stop ()
134 if (session->transport_rolling ()) {
137 AccessAction ("Transport", "GotoStart");
142 FaderPort8::button_record ()
144 set_record_enable (!get_record_enabled ());
148 FaderPort8::button_loop ()
154 FaderPort8::button_metronom ()
156 Config->set_clicking (!Config->get_clicking ());
160 FaderPort8::button_automation (ARDOUR::AutoState as)
162 FaderMode fadermode = _ctrls.fader_mode ();
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);
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;
177 while (0 != (send = s->send_level_controllable (i))) {
178 send->set_automation_state (as);
188 // apply to all selected tracks
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()) {
195 if (!(*i)->is_selected()) {
198 boost::shared_ptr<AutomationControl> ac;
201 ac = (*i)->gain_control ();
204 ac = (*i)->pan_azimuth_control ();
210 ac->set_automation_state (as);
216 FaderPort8::button_varispeed (bool ffw)
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 ()){
223 dynamic_cast<FP8RepeatButton*>(&b_ffw)->stop_repeat();
224 dynamic_cast<FP8RepeatButton*>(&b_rew)->stop_repeat();
225 session->request_locate (0, false);
229 // switch play direction, if needed
231 if (session->transport_speed () <= 0) {
232 session->request_transport_speed (1.0);
236 if (session->transport_speed () >= 0) {
237 session->request_transport_speed (-1.0);
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);
249 #ifdef FP8_MUTESOLO_UNDO
251 FaderPort8::button_solo_clear ()
253 bool soloing = session->soloing() || session->listening();
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()) {
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));
266 AccessAction ("Main", "cancel-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();
275 if (ac->automation_state() == Touch && !ac->touching ()) {
276 ac->start_touch (ac->session().transport_frame());
281 session->set_controls (cl, 1.0, PBD::Controllable::NoGroup);
288 FaderPort8::button_mute_clear ()
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()) {
297 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(*i);
298 if (r && !r->active()) {
301 boost::shared_ptr<AutomationControl> ac = (*i)->mute_control();
302 if (ac && ac->get_value () > 0) {
308 #ifdef FP8_MUTESOLO_UNDO
311 for (std::vector <boost::weak_ptr<AutomationControl> >::const_iterator i = _mute_state.begin(); i != _mute_state.end(); ++i) {
312 boost::shared_ptr<AutomationControl> ac = (*i).lock();
319 /* save muted control IDs */
320 _mute_state.clear ();
321 for (ControlList::const_iterator i = cl->begin (); i != cl->end (); ++i) {
322 _mute_state.push_back (boost::weak_ptr<AutomationControl>(*i));
331 for (ControlList::const_iterator i = cl->begin (); i != cl->end (); ++i) {
332 if ((*i)->automation_state() == Touch && !(*i)->touching ()) {
333 (*i)->start_touch ((*i)->session().transport_frame());
337 session->set_controls (cl, mute ? 1.0 : 0.0, mute ? PBD::Controllable::NoGroup : PBD::Controllable::UseGroup);
341 FaderPort8::button_arm_all ()
343 BasicUI::all_tracks_rec_in ();
346 /* access generic action */
348 FaderPort8::button_action (const std::string& group, const std::string& item)
350 AccessAction (group, item);
353 /* ****************************************************************************
354 * Mode specific and internal callbacks
357 /* handle "ARM" press -- act like shift, change "Select" button mode */
359 FaderPort8::button_arm (bool press)
361 FaderMode fadermode = _ctrls.fader_mode ();
362 if (fadermode == ModeTrack || fadermode == ModePan) {
363 _ctrls.button (FP8Controls::BtnArm).set_active (press);
364 ARMButtonChange (press); /* EMIT SIGNAL */
369 FaderPort8::button_prev_next (bool next)
371 switch (_ctrls.nav_mode()) {
373 select_prev_next (next);
384 VerticalZoomInSelected ();
386 VerticalZoomOutSelected ();
391 AccessAction ("Region", "nudge-forward");
393 AccessAction ("Region", "nudge-backward");
406 /* handle navigation encoder press */
408 FaderPort8::button_encoder ()
410 /* special-case metronome level */
411 if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
412 Config->set_click_gain (1.0);
413 _ctrls.button (FP8Controls::BtnClick).ignore_release();
416 switch (_ctrls.nav_mode()) {
418 ZoomToSession (); // XXX undo zoom
424 AccessAction ("Editor", "select-topmost");
427 move_selected_into_view ();
431 /* master || monitor level -- reset to 0dB */
432 boost::shared_ptr<AutomationControl> ac;
433 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
434 ac = session->monitor_out()->gain_control ();
435 } else if (session->master_out()) {
436 ac = session->master_out()->gain_control ();
439 if (ac->automation_state() == Touch && !ac->touching ()) {
440 ac->start_touch (ac->session().transport_frame());
442 ac->set_value (ac->normal(), PBD::Controllable::NoGroup);
452 /* Don't add another mark if one exists within 1/100th of a second of
453 * the current position and we're not rolling.
455 framepos_t where = session->audible_frame();
456 if (session->transport_stopped() && session->locations()->mark_at (where, session->frame_rate() / 100.0)) {
460 session->locations()->next_available_name (markername,"mark");
461 add_marker (markername);
467 /* handle navigation encoder turn */
469 FaderPort8::encoder_navigate (bool neg, int steps)
471 /* special-case metronome level */
472 if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
473 // compare to ARDOUR_UI::click_button_scroll()
474 gain_t gain = Config->get_click_gain();
475 float gain_db = accurate_coefficient_to_dB (gain);
476 gain_db += (neg ? -1.f : 1.f) * steps;
477 gain_db = std::max (-60.f, gain_db);
478 gain = dB_to_coefficient (gain_db);
479 gain = std::min (gain, Config->get_max_gain());
480 Config->set_click_gain (gain);
481 _ctrls.button (FP8Controls::BtnClick).ignore_release();
485 switch (_ctrls.nav_mode()) {
488 AccessAction ("Mixer", "scroll-left");
489 AccessAction ("Editor", "step-tracks-up");
491 AccessAction ("Mixer", "scroll-right");
492 AccessAction ("Editor", "step-tracks-down");
504 ScrollTimeline ((neg ? -1.f : 1.f) * steps / (shift_mod() ? 1024.f : 256.f));
511 /* master || monitor level */
512 boost::shared_ptr<AutomationControl> ac;
513 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
514 ac = session->monitor_out()->gain_control ();
515 } else if (session->master_out()) {
516 ac = session->master_out()->gain_control ();
519 double v = ac->internal_to_interface (ac->get_value());
520 v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
521 if (ac->automation_state() == Touch && !ac->touching ()) {
522 ac->start_touch (ac->session().transport_frame());
524 ac->set_value (ac->interface_to_internal(v), PBD::Controllable::NoGroup);
530 AccessAction ("Common", "nudge-playhead-backward");
532 AccessAction ("Common", "nudge-playhead-forward");
538 /* handle pan/param encoder press */
540 FaderPort8::button_parameter ()
542 switch (_ctrls.fader_mode()) {
546 boost::shared_ptr<Stripable> s = first_selected_stripable();
548 boost::shared_ptr<AutomationControl> ac;
549 if (shift_mod () || _ctrls.fader_mode() == ModePan) {
550 ac = s->pan_width_control ();
552 ac = s->pan_azimuth_control ();
555 if (ac->automation_state() == Touch && !ac->touching ()) {
556 ac->start_touch (ac->session().transport_frame());
558 ac->set_value (ac->normal(), PBD::Controllable::UseGroup);
570 /* handle pan/param encoder turn */
572 FaderPort8::encoder_parameter (bool neg, int steps)
574 switch (_ctrls.fader_mode()) {
578 boost::shared_ptr<Stripable> s = first_selected_stripable();
580 boost::shared_ptr<AutomationControl> ac;
581 if (shift_mod () || _ctrls.fader_mode() == ModePan) {
582 ac = s->pan_width_control ();
584 ac = s->pan_azimuth_control ();
587 double v = ac->internal_to_interface (ac->get_value());
588 v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
589 if (ac->automation_state() == Touch && !ac->touching ()) {
590 ac->start_touch (ac->session().transport_frame());
592 ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
600 bank_param (neg, false);
607 /* handle user-specific actions */
609 FaderPort8::button_user (bool press, FP8Controls::ButtonId btn)
611 _user_action_map[btn].call (*this, press);