FP8: ignore inactive muted tracks w/mute-clear
[ardour.git] / libs / surfaces / faderport8 / callbacks.cc
1 /* Faderport 8 Control Surface
2  * This is the button "View" of the MVC surface inteface,
3  * see actions.cc for the "Controller"
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/session.h"
23 #include "ardour/session_configuration.h"
24
25 #include "gtkmm2ext/actions.h"
26
27 #include "faderport8.h"
28
29 #include "pbd/i18n.h"
30
31 using namespace ARDOUR;
32 using namespace ArdourSurface;
33 using namespace ArdourSurface::FP8Types;
34
35 void
36 FaderPort8::connect_session_signals ()
37 {
38          session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_stripable_added_or_removed, this), this);
39          PresentationInfo::Change.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_pi_property_changed, this, _1), this);
40
41         Config->ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_parameter_changed, this, _1), this);
42         session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_parameter_changed, this, _1), this);
43
44         session->TransportStateChange.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_transport_state_changed, this), this);
45         session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_loop_state_changed, this), this);
46         session->RecordStateChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_record_state_changed, this), this);
47
48         session->DirtyChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_session_dirty_changed, this), this);
49         session->SoloChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_solo_changed, this), this);
50         session->MuteChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_mute_changed, this), this);
51         session->history().Changed.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_history_changed, this), this);
52 }
53
54 void
55 FaderPort8::send_session_state ()
56 {
57         notify_transport_state_changed ();
58         notify_record_state_changed ();
59         notify_session_dirty_changed ();
60         notify_history_changed ();
61         notify_solo_changed ();
62         notify_mute_changed ();
63         notify_parameter_changed ("clicking");
64
65         notify_automation_mode_changed (); // XXX (stip specific, see below)
66 }
67
68 // TODO: AutomationState display of plugin & send automation ?!
69 void
70 FaderPort8::notify_automation_mode_changed ()
71 {
72         boost::shared_ptr<Stripable> s = first_selected_stripable();
73         boost::shared_ptr<AutomationControl> ac;
74         if (s) {
75                 switch (_ctrls.fader_mode ()) {
76                         case ModeTrack:
77                                 ac = s->gain_control();
78                                 break;
79                         case ModePan:
80                                 ac = s->pan_azimuth_control();
81                                 break;
82                         default:
83                                 break;
84                 }
85         }
86         if (!s || !ac) {
87                 _ctrls.button (FP8Controls::BtnALatch).set_active (false);
88                 _ctrls.button (FP8Controls::BtnATrim).set_active (false);
89                 _ctrls.button (FP8Controls::BtnAOff).set_active (false);
90                 _ctrls.button (FP8Controls::BtnATouch).set_active (false);
91                 _ctrls.button (FP8Controls::BtnARead).set_active (false);
92                 _ctrls.button (FP8Controls::BtnAWrite).set_active (false);
93                 return;
94         }
95
96         ARDOUR::AutoState as = ac->automation_state();
97         _ctrls.button (FP8Controls::BtnAOff).set_active (as == Off);
98         _ctrls.button (FP8Controls::BtnATouch).set_active (as == Touch);
99         _ctrls.button (FP8Controls::BtnARead).set_active (as == Play);
100         _ctrls.button (FP8Controls::BtnAWrite).set_active (as == Write);
101 }
102
103 void
104 FaderPort8::notify_parameter_changed (std::string param)
105 {
106         if (param == "clicking") {
107                 _ctrls.button (FP8Controls::BtnClick).set_active (Config->get_clicking ());
108         }
109 }
110
111 void
112 FaderPort8::notify_transport_state_changed ()
113 {
114         if (session->transport_rolling ()) {
115                 _ctrls.button (FP8Controls::BtnPlay).set_active (true);
116                 _ctrls.button (FP8Controls::BtnStop).set_active (false);
117         } else {
118                 _ctrls.button (FP8Controls::BtnPlay).set_active (false);
119                 _ctrls.button (FP8Controls::BtnStop).set_active (true);
120         }
121
122         /* set rewind/fastforward lights */
123         const float ts = session->transport_speed ();
124         FP8ButtonInterface& b_rew = _ctrls.button (FP8Controls::BtnRewind);
125         FP8ButtonInterface& b_ffw = _ctrls.button (FP8Controls::BtnFastForward);
126
127         const bool rew = (ts < 0.f);
128         const bool ffw = (ts > 0.f && ts != 1.f);
129         if (b_rew.is_active() != rew) {
130                 b_rew.set_active (rew);
131         }
132         if (b_ffw.is_active() != ffw) {
133                 b_ffw.set_active (ffw);
134         }
135
136         notify_loop_state_changed ();
137 }
138
139 void
140 FaderPort8::notify_record_state_changed ()
141 {
142         switch (session->record_status ()) {
143                 case Session::Disabled:
144                         _ctrls.button (FP8Controls::BtnRecord).set_active (0);
145                         _ctrls.button (FP8Controls::BtnRecord).set_blinking (false);
146                         break;
147                 case Session::Enabled:
148                         _ctrls.button (FP8Controls::BtnRecord).set_active (true);
149                         _ctrls.button (FP8Controls::BtnRecord).set_blinking (true);
150                         break;
151                 case Session::Recording:
152                         _ctrls.button (FP8Controls::BtnRecord).set_active (true);
153                         _ctrls.button (FP8Controls::BtnRecord).set_blinking (false);
154                         break;
155         }
156 }
157
158 void
159 FaderPort8::notify_loop_state_changed ()
160 {
161         bool looping = false;
162         Location* looploc = session->locations ()->auto_loop_location ();
163         if (looploc && session->get_play_loop ()) {
164                 looping = true;
165         }
166         _ctrls.button (FP8Controls::BtnLoop).set_active (looping);
167 }
168
169 void
170 FaderPort8::notify_session_dirty_changed ()
171 {
172         const bool is_dirty = session->dirty ();
173         _ctrls.button (FP8Controls::BtnSave).set_active (is_dirty);
174         _ctrls.button (FP8Controls::BtnSave).set_color (is_dirty ? 0xff0000ff : 0x00ff00ff);
175 }
176
177 void
178 FaderPort8::notify_history_changed ()
179 {
180         _ctrls.button (FP8Controls::BtnRedo).set_active (session->redo_depth() > 0);
181         _ctrls.button (FP8Controls::BtnUndo).set_active (session->undo_depth() > 0);
182 }
183
184 void
185 FaderPort8::notify_solo_changed ()
186 {
187         bool soloing = session->soloing() || session->listening();
188         _ctrls.button (FP8Controls::BtnSoloClear).set_active (soloing);
189 #ifdef FP8_MUTESOLO_UNDO
190         if (soloing) {
191                 _solo_state.clear ();
192         }
193 #endif
194 }
195
196 void
197 FaderPort8::notify_mute_changed ()
198 {
199         bool muted = false;
200         StripableList all;
201         session->get_stripables (all);
202         for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
203                 if ((*i)->is_auditioner() || (*i)->is_monitor()) {
204                         continue;
205                 }
206                 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(*i);
207                 if (r && !r->active()) {
208                         continue;
209                 }
210                 boost::shared_ptr<MuteControl> mc = (*i)->mute_control();
211                 if (mc && mc->muted ()) {
212                         muted = true;
213                         break;
214                 }
215         }
216 #ifdef FP8_MUTESOLO_UNDO
217         if (muted) {
218                 _mute_state.clear ();
219         }
220 #endif
221         _ctrls.button (FP8Controls::BtnMuteClear).set_active (muted);
222 }