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/plugin_insert.h"
24 #include "ardour/session.h"
25 #include "ardour/session_configuration.h"
26 #include "ardour/types.h"
28 #include "gtkmm2ext/actions.h"
30 #include "faderport8.h"
35 using namespace ARDOUR;
36 using namespace ArdourSurface::FP_NAMESPACE;
37 using namespace ArdourSurface::FP_NAMESPACE::FP8Types;
39 #define BindMethod(ID, CB) \
40 _ctrls.button (FP8Controls::ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this));
42 #define BindFunction(ID, ACT, CB, ...) \
43 _ctrls.button (FP8Controls::ID). ACT .connect_same_thread (button_connections, boost::bind (&FaderPort8:: CB, this, __VA_ARGS__));
45 #define BindAction(ID, GRP, ITEM) \
46 _ctrls.button (FP8Controls::ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_action, this, GRP, ITEM));
48 #define BindUserAction(ID) \
49 _ctrls.button (ID).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_user, this, true, ID)); \
50 _ctrls.button (ID).released.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_user, this, false, ID));
53 /* Bind button signals (press, release) to callback methods
54 * (called once after constructing buttons).
55 * Bound actions are handled the the ctrl-surface thread.
58 FaderPort8::setup_actions ()
60 BindMethod (BtnPlay, button_play);
61 BindMethod (BtnStop, button_stop);
62 BindMethod (BtnLoop, button_loop);
63 BindMethod (BtnRecord, button_record);
64 BindMethod (BtnClick, button_metronom);
65 BindAction (BtnRedo, "Editor", "redo");
67 BindAction (BtnSave, "Common", "Save");
68 BindAction (BtnUndo, "Editor", "undo");
69 BindAction (BtnRedo, "Editor", "redo");
71 #ifdef FP8_MUTESOLO_UNDO
72 BindMethod (BtnSoloClear, button_solo_clear);
74 BindAction (BtnSoloClear, "Main", "cancel-solo");
76 BindMethod (BtnMuteClear, button_mute_clear);
78 BindMethod (FP8Controls::BtnArmAll, button_arm_all);
80 BindFunction (BtnRewind, pressed, button_varispeed, false);
81 BindFunction (BtnFastForward, pressed, button_varispeed, true);
83 BindFunction (BtnPrev, released, button_prev_next, false);
84 BindFunction (BtnNext, released, button_prev_next, true);
86 BindFunction (BtnArm, pressed, button_arm, true);
87 BindFunction (BtnArm, released, button_arm, false);
89 BindFunction (BtnAOff, released, button_automation, ARDOUR::Off);
90 BindFunction (BtnATouch, released, button_automation, ARDOUR::Touch);
91 BindFunction (BtnARead, released, button_automation, ARDOUR::Play);
92 BindFunction (BtnAWrite, released, button_automation, ARDOUR::Write);
93 BindFunction (BtnALatch, released, button_automation, ARDOUR::Latch);
95 _ctrls.button (FP8Controls::BtnEncoder).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_encoder, this));
96 _ctrls.button (FP8Controls::BtnParam).pressed.connect_same_thread (button_connections, boost::bind (&FaderPort8::button_parameter, this));
99 BindMethod (BtnBypass, button_bypass);
100 BindAction (BtnBypassAll, "Mixer", "ab-plugins");
102 BindAction (BtnMacro, "Common", "toggle-editor-and-mixer");
103 BindMethod (BtnOpen, button_open);
105 BindMethod (BtnLink, button_link);
106 BindMethod (BtnLock, button_lock);
109 for (FP8Controls::UserButtonMap::const_iterator i = _ctrls.user_buttons ().begin ();
110 i != _ctrls.user_buttons ().end (); ++i) {
111 BindUserAction ((*i).first);
115 /* ****************************************************************************
116 * Direct control callback Actions
120 FaderPort8::button_play ()
122 if (session->transport_rolling ()) {
123 if (session->transport_speed () != 1.0) {
124 session->request_transport_speed (1.0);
134 FaderPort8::button_stop ()
136 if (session->transport_rolling ()) {
139 AccessAction ("Transport", "GotoStart");
144 FaderPort8::button_record ()
146 set_record_enable (!get_record_enabled ());
150 FaderPort8::button_loop ()
156 FaderPort8::button_metronom ()
158 Config->set_clicking (!Config->get_clicking ());
162 FaderPort8::button_bypass ()
164 boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock();
166 pi->enable (! pi->enabled ());
168 AccessAction ("Mixer", "ab-plugins");
173 FaderPort8::button_open ()
175 boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock();
177 pi->ToggleUI (); /* EMIT SIGNAL */
179 AccessAction ("Common", "addExistingAudioFiles");
183 FaderPort8::button_lock ()
185 if (!_link_enabled) {
186 AccessAction ("Editor", "lock");
191 } else if (!_link_control.expired ()) {
197 FaderPort8::button_link ()
199 switch (_ctrls.fader_mode()) {
209 //AccessAction ("Window", "show-mixer");
215 FaderPort8::button_automation (ARDOUR::AutoState as)
217 FaderMode fadermode = _ctrls.fader_mode ();
220 #if 0 // Plugin Control Automation Mode
221 for ( std::list <ProcessorCtrl>::iterator i = _proc_params.begin(); i != _proc_params.end(); ++i) {
222 ((*i).ac)->set_automation_state (as);
227 if (first_selected_stripable()) {
228 #if 0 // Send Level Automation
229 boost::shared_ptr<Stripable> s = first_selected_stripable();
230 boost::shared_ptr<AutomationControl> send;
232 while (0 != (send = s->send_level_controllable (i))) {
233 send->set_automation_state (as);
243 // TODO link/lock control automation?
245 // apply to all selected tracks
247 session->get_stripables (all);
248 for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
249 if ((*i)->is_master() || (*i)->is_monitor()) {
252 if (!(*i)->is_selected()) {
255 boost::shared_ptr<AutomationControl> ac;
258 ac = (*i)->gain_control ();
261 ac = (*i)->pan_azimuth_control ();
267 ac->set_automation_state (as);
273 FaderPort8::button_varispeed (bool ffw)
275 /* pressing both rew + ffwd -> return to zero */
276 FP8ButtonInterface& b_rew = _ctrls.button (FP8Controls::BtnRewind);
277 FP8ButtonInterface& b_ffw = _ctrls.button (FP8Controls::BtnFastForward);
278 if (b_rew.is_pressed () && b_ffw.is_pressed ()){
280 dynamic_cast<FP8RepeatButton*>(&b_ffw)->stop_repeat();
281 dynamic_cast<FP8RepeatButton*>(&b_rew)->stop_repeat();
282 session->request_locate (0, false);
286 // switch play direction, if needed
288 if (session->transport_speed () <= 0) {
289 session->request_transport_speed (1.0);
293 if (session->transport_speed () >= 0) {
294 session->request_transport_speed (-1.0);
298 // incremetally increase speed. double speed every 10 clicks
299 // (keypress auto-repeat is 100ms)
300 float maxspeed = Config->get_shuttle_max_speed();
301 float speed = exp2f(0.1f) * session->transport_speed ();
302 speed = std::max (-maxspeed, std::min (maxspeed, speed));
303 session->request_transport_speed (speed, false);
306 #ifdef FP8_MUTESOLO_UNDO
308 FaderPort8::button_solo_clear ()
310 bool soloing = session->soloing() || session->listening();
312 soloing |= session->mixbus_soloed();
316 session->get_stripables (all);
317 for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
318 if ((*i)->is_master() || (*i)->is_auditioner() || (*i)->is_monitor()) {
321 boost::shared_ptr<SoloControl> sc = (*i)->solo_control();
322 if (sc && sc->self_soloed ()) {
323 _solo_state.push_back (boost::weak_ptr<AutomationControl>(sc));
326 cancel_all_solo (); // AccessAction ("Main", "cancel-solo");
329 boost::shared_ptr<ControlList> cl (new ControlList);
330 for (std::vector <boost::weak_ptr<AutomationControl> >::const_iterator i = _solo_state.begin(); i != _solo_state.end(); ++i) {
331 boost::shared_ptr<AutomationControl> ac = (*i).lock();
335 ac->start_touch (ac->session().transport_sample());
339 session->set_controls (cl, 1.0, PBD::Controllable::NoGroup);
346 FaderPort8::button_mute_clear ()
348 #ifdef FP8_MUTESOLO_UNDO
349 if (session->muted ()) {
350 _mute_state = session->cancel_all_mute ();
353 boost::shared_ptr<ControlList> cl (new ControlList);
354 for (std::vector <boost::weak_ptr<AutomationControl> >::const_iterator i = _mute_state.begin(); i != _mute_state.end(); ++i) {
355 boost::shared_ptr<AutomationControl> ac = (*i).lock();
360 ac->start_touch (ac->session().transport_sample());
363 session->set_controls (cl, 1.0, PBD::Controllable::NoGroup);
367 session->cancel_all_mute ();
372 FaderPort8::button_arm_all ()
374 BasicUI::all_tracks_rec_in ();
377 /* access generic action */
379 FaderPort8::button_action (const std::string& group, const std::string& item)
381 AccessAction (group, item);
384 /* ****************************************************************************
385 * Control Interaction (encoder)
389 FaderPort8::handle_encoder_pan (int steps)
391 boost::shared_ptr<Stripable> s = first_selected_stripable();
393 boost::shared_ptr<AutomationControl> ac;
394 if (shift_mod () || _ctrls.fader_mode() == ModePan) {
395 ac = s->pan_width_control ();
397 ac = s->pan_azimuth_control ();
400 ac->start_touch (ac->session().transport_sample());
402 ac->set_value (ac->normal(), PBD::Controllable::UseGroup);
404 double v = ac->internal_to_interface (ac->get_value());
405 v = std::max (0.0, std::min (1.0, v + steps * .01));
406 ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
413 FaderPort8::handle_encoder_link (int steps)
415 if (_link_control.expired ()) {
418 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (_link_control.lock ());
423 double v = ac->internal_to_interface (ac->get_value());
424 ac->start_touch (ac->session().transport_sample());
427 ac->set_value (ac->normal(), PBD::Controllable::UseGroup);
431 if (ac->desc().toggled) {
433 } else if (ac->desc().integer_step) {
434 v += steps / (1.f + ac->desc().upper - ac->desc().lower);
435 } else if (ac->desc().enumeration) {
436 ac->set_value (ac->desc().step_enum (ac->get_value(), steps < 0), PBD::Controllable::UseGroup);
439 v = std::max (0.0, std::min (1.0, v + steps * .01));
441 ac->set_value (ac->interface_to_internal(v), PBD::Controllable::UseGroup);
445 /* ****************************************************************************
446 * Mode specific and internal callbacks
449 /* handle "ARM" press -- act like shift, change "Select" button mode */
451 FaderPort8::button_arm (bool press)
453 FaderMode fadermode = _ctrls.fader_mode ();
454 if (fadermode == ModeTrack || fadermode == ModePan) {
455 _ctrls.button (FP8Controls::BtnArm).set_active (press);
456 ARMButtonChange (press); /* EMIT SIGNAL */
461 FaderPort8::button_prev_next (bool next)
463 switch (_ctrls.nav_mode()) {
465 select_prev_next (next);
476 VerticalZoomInSelected ();
478 VerticalZoomOutSelected ();
483 AccessAction ("Region", "nudge-forward");
485 AccessAction ("Region", "nudge-backward");
498 /* handle navigation encoder press */
500 FaderPort8::button_encoder ()
502 /* special-case metronome level */
503 if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
504 Config->set_click_gain (1.0);
505 _ctrls.button (FP8Controls::BtnClick).ignore_release();
508 switch (_ctrls.nav_mode()) {
510 ZoomToSession (); // XXX undo zoom
516 AccessAction ("Editor", "select-topmost");
519 move_selected_into_view ();
523 /* master || monitor level -- reset to 0dB */
524 boost::shared_ptr<AutomationControl> ac;
525 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
526 ac = session->monitor_out()->gain_control ();
527 } else if (session->master_out()) {
528 ac = session->master_out()->gain_control ();
531 ac->start_touch (ac->session().transport_sample());
532 ac->set_value (ac->normal(), PBD::Controllable::NoGroup);
542 /* Don't add another mark if one exists within 1/100th of a second of
543 * the current position and we're not rolling.
545 samplepos_t where = session->audible_sample();
546 if (session->transport_stopped() && session->locations()->mark_at (where, session->sample_rate() / 100.0)) {
550 session->locations()->next_available_name (markername,"mark");
551 add_marker (markername);
557 /* handle navigation encoder turn */
559 FaderPort8::encoder_navigate (bool neg, int steps)
561 /* special-case metronome level */
562 if (_ctrls.button (FP8Controls::BtnClick).is_pressed ()) {
563 // compare to ARDOUR_UI::click_button_scroll()
564 gain_t gain = Config->get_click_gain();
565 float gain_db = accurate_coefficient_to_dB (gain);
566 gain_db += (neg ? -1.f : 1.f) * steps;
567 gain_db = std::max (-60.f, gain_db);
568 gain = dB_to_coefficient (gain_db);
569 gain = std::min (gain, Config->get_max_gain());
570 Config->set_click_gain (gain);
571 _ctrls.button (FP8Controls::BtnClick).ignore_release();
575 switch (_ctrls.nav_mode()) {
578 AccessAction ("Mixer", "scroll-left");
579 AccessAction ("Editor", "step-tracks-up");
581 AccessAction ("Mixer", "scroll-right");
582 AccessAction ("Editor", "step-tracks-down");
594 ScrollTimeline ((neg ? -1.f : 1.f) * steps / (shift_mod() ? 1024.f : 256.f));
601 /* master || monitor level */
602 boost::shared_ptr<AutomationControl> ac;
603 if (session->monitor_active() && !_ctrls.button (FP8Controls::BtnMaster).is_pressed ()) {
604 ac = session->monitor_out()->gain_control ();
605 } else if (session->master_out()) {
606 ac = session->master_out()->gain_control ();
609 double v = ac->internal_to_interface (ac->get_value());
610 v = std::max (0.0, std::min (1.0, v + steps * (neg ? -.01 : .01)));
611 ac->start_touch (ac->session().transport_sample());
612 ac->set_value (ac->interface_to_internal(v), PBD::Controllable::NoGroup);
618 AccessAction ("Common", "nudge-playhead-backward");
620 AccessAction ("Common", "nudge-playhead-forward");
626 /* handle pan/param encoder press */
628 FaderPort8::button_parameter ()
630 switch (_ctrls.fader_mode()) {
633 if (_link_enabled || _link_locked) {
634 handle_encoder_link (0);
636 handle_encoder_pan (0);
640 toggle_preset_param_mode ();
647 /* handle pan/param encoder turn */
649 FaderPort8::encoder_parameter (bool neg, int steps)
651 switch (_ctrls.fader_mode()) {
655 if (_link_enabled || _link_locked) {
656 handle_encoder_link (neg ? -steps : steps);
658 handle_encoder_pan (neg ? -steps : steps);
665 bank_param (neg, shift_mod());
672 /* handle user-specific actions */
674 FaderPort8::button_user (bool press, FP8Controls::ButtonId btn)
676 _user_action_map[btn].call (*this, press);