7ce1dfe5d465d4b27c05b5bc2f28a1c424ecf758
[ardour.git] / libs / surfaces / faderport8 / fp8_controls.cc
1 /* Faderport 8 Control Surface
2  * Abstraction of Surface Control Elements.
3  *
4  * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20
21 #include "fp8_controls.h"
22
23 using namespace ArdourSurface;
24 using namespace ArdourSurface::FP8Types;
25
26 bool FP8ButtonInterface::force_change = false;
27
28 #define NEWBUTTON(midi_id, button_id, color)            \
29   do {                                                  \
30   assert (_midimap.end() == _midimap.find (midi_id));   \
31   assert (_ctrlmap.end() == _ctrlmap.find (button_id)); \
32   FP8Button *t = new FP8Button (b, midi_id);            \
33   _midimap[midi_id] = t;                                \
34   _ctrlmap[button_id] = t;                              \
35   } while (0)
36
37
38 #define NEWTYPEBUTTON(TYPE, midi_id, button_id, color)  \
39   do {                                                  \
40   assert (_midimap.end() == _midimap.find (midi_id));   \
41   assert (_ctrlmap.end() == _ctrlmap.find (button_id)); \
42   TYPE *t = new TYPE (b, midi_id);                      \
43   _midimap[midi_id] = t;                                \
44   _ctrlmap[button_id] = t;                              \
45   } while (0)
46
47
48
49 #define NEWSHIFTBUTTON(midi_id, id1, id2, color)        \
50   do {                                                  \
51   assert (_midimap.end() == _midimap.find (midi_id));   \
52   assert (_ctrlmap.end() == _ctrlmap.find (id1));       \
53   assert (_ctrlmap.end() == _ctrlmap.find (id2));       \
54   FP8ShiftSensitiveButton *t =                          \
55     new FP8ShiftSensitiveButton (b, midi_id, color);    \
56   _midimap[midi_id] = t;                                \
57   _ctrlmap[id1] = t->button ();                         \
58   _ctrlmap[id2] = t->button_shift ();                   \
59   } while (0)
60
61
62 FP8Controls::FP8Controls (FP8Base& b)
63         : _fadermode (ModeTrack)
64         , _navmode (NavMaster)
65         , _mixmode (MixAll)
66         , _display_timecode (false)
67 {
68         NEWBUTTON (0x56, BtnLoop, false);
69         NEWTYPEBUTTON (FP8RepeatButton, 0x5b, BtnRewind, false);
70         NEWTYPEBUTTON (FP8RepeatButton, 0x5c, BtnFastForward, false);
71         NEWBUTTON (0x5d, BtnStop, false);
72         NEWBUTTON (0x5e, BtnPlay, false);
73         NEWBUTTON (0x5f, BtnRecord, false);
74
75         NEWSHIFTBUTTON (0x4a, BtnARead, BtnUser3, true);
76         NEWSHIFTBUTTON (0x4b, BtnAWrite, BtnUser2, true);
77         NEWSHIFTBUTTON (0x4c, BtnATrim, BtnRedo, true);
78         NEWSHIFTBUTTON (0x4d, BtnATouch, BtnUser1, true);
79         NEWSHIFTBUTTON (0x4e, BtnALatch, BtnSave, true);
80         NEWSHIFTBUTTON (0x4f, BtnAOff, BtnUndo, true);
81
82         NEWBUTTON (0x2e, BtnPrev, false);
83         NEWBUTTON (0x2f, BtnNext, false);
84
85         NEWSHIFTBUTTON (0x36, BtnChannel, BtnF1, false);
86         NEWSHIFTBUTTON (0x37, BtnZoom,    BtnF2, false);
87         NEWSHIFTBUTTON (0x38, BtnScroll,  BtnF3, false);
88         NEWSHIFTBUTTON (0x39, BtnBank,    BtnF4, false);
89         NEWSHIFTBUTTON (0x3a, BtnMaster,  BtnF5, false);
90         NEWSHIFTBUTTON (0x3b, BtnClick,   BtnF6, false);
91         NEWSHIFTBUTTON (0x3c, BtnSection, BtnF7, false);
92         NEWSHIFTBUTTON (0x3d, BtnMarker,  BtnF8, false);
93
94         NEWSHIFTBUTTON (0x28, BtnTrack, BtnTimecode, false);
95         NEWBUTTON (0x2b, BtnPlugins, false);
96         NEWBUTTON (0x29, BtnSend, false);
97         NEWBUTTON (0x2a, BtnPan, false);
98
99         NEWSHIFTBUTTON (0x00, BtnArm, BtnArmAll, false);
100         NEWBUTTON (0x01, BtnSoloClear, false);
101         NEWBUTTON (0x02, BtnMuteClear, false);
102
103         NEWSHIFTBUTTON (0x03, BtnBypass, BtnBypassAll, true);
104         NEWSHIFTBUTTON (0x04, BtnMacro, BtnOpen, true);
105         NEWSHIFTBUTTON (0x05, BtnLink, BtnLock, true);
106
107         NEWSHIFTBUTTON (0x3e, BtnMAudio, BtnMInputs, true);
108         NEWSHIFTBUTTON (0x3f, BtnMVI, BtnMMIDI, true);
109         NEWSHIFTBUTTON (0x40, BtnMBus, BtnMOutputs, true);
110         NEWSHIFTBUTTON (0x41, BtnMVCA, BtnMFX, true);
111         NEWSHIFTBUTTON (0x42, BtnMAll, BtnMUser, true);
112
113         NEWTYPEBUTTON (FP8ReadOnlyButton, 0x53, BtnEncoder, false);
114         NEWTYPEBUTTON (FP8ReadOnlyButton, 0x20, BtnParam, false);
115         NEWTYPEBUTTON (FP8ReadOnlyButton, 0x66, BtnFootswitch, false);
116
117         /* internal bindings */
118
119 #define BindMethod(ID, CB) \
120         button (ID).released.connect_same_thread (button_connections, boost::bind (&FP8Controls:: CB, this));
121
122         BindMethod (FP8Controls::BtnTimecode, toggle_timecode);
123
124 #define BindNav(BTN, MODE)\
125         button (BTN).released.connect_same_thread (button_connections, boost::bind (&FP8Controls::set_nav_mode, this, MODE))
126
127         BindNav (BtnChannel, NavChannel);
128         BindNav (BtnZoom,    NavZoom);
129         BindNav (BtnScroll,  NavScroll);
130         BindNav (BtnBank,    NavBank);
131         BindNav (BtnMaster,  NavMaster);
132         BindNav (BtnSection, NavSection);
133         BindNav (BtnMarker,  NavMarker);
134
135 #define BindFader(BTN, MODE)\
136         button (BTN).released.connect_same_thread (button_connections, boost::bind (&FP8Controls::set_fader_mode, this, MODE))
137
138         BindFader (BtnTrack,   ModeTrack);
139         BindFader (BtnPlugins, ModePlugins);
140         BindFader (BtnSend,    ModeSend);
141         BindFader (BtnPan,     ModePan);
142
143
144 #define BindMix(BTN, MODE)\
145         button (BTN).released.connect_same_thread (button_connections, boost::bind (&FP8Controls::set_mix_mode, this, MODE))
146
147         BindMix (BtnMAudio,   MixAudio);
148         BindMix (BtnMVI,      MixInstrument);
149         BindMix (BtnMBus,     MixBus);
150         BindMix (BtnMVCA,     MixVCA);
151         BindMix (BtnMAll,     MixAll);
152         BindMix (BtnMInputs,  MixInputs);
153         BindMix (BtnMMIDI,    MixMIDI);
154         BindMix (BtnMOutputs, MixOutputs);
155         BindMix (BtnMFX,      MixFX);
156         BindMix (BtnMUser,    MixUser);
157
158         /* create channelstrips */
159         for (uint8_t id = 0; id < 8; ++id) {
160                 chanstrip[id] = new FP8Strip (b, id);
161                 _midimap_strip[0x08 + id] = &(chanstrip[id]->solo_button());
162                 _midimap_strip[0x10 + id] = &(chanstrip[id]->mute_button());
163                 _midimap_strip[0x18 + id] = &(chanstrip[id]->selrec_button());
164         }
165
166         /* set User button names */
167
168 #define REGISTER_ENUM(ID, NAME) \
169         _user_str_to_enum[#ID] = ID; \
170         _user_enum_to_str[ID]  = #ID; \
171         _user_buttons[ID]      = NAME;
172
173         REGISTER_ENUM (BtnFootswitch, "Footswitch");
174         REGISTER_ENUM (BtnUser1     , "User 1");
175         REGISTER_ENUM (BtnUser2     , "User 2");
176         REGISTER_ENUM (BtnUser3     , "User 3");
177         REGISTER_ENUM (BtnF1        , "F1");
178         REGISTER_ENUM (BtnF2        , "F2");
179         REGISTER_ENUM (BtnF3        , "F3");
180         REGISTER_ENUM (BtnF4        , "F4");
181         REGISTER_ENUM (BtnF5        , "F5");
182         REGISTER_ENUM (BtnF6        , "F6");
183         REGISTER_ENUM (BtnF7        , "F7");
184         REGISTER_ENUM (BtnF8        , "F8");
185 #undef REGISTER_ENUM
186 }
187
188 FP8Controls::~FP8Controls ()
189 {
190         for (MidiButtonMap::const_iterator i = _midimap.begin (); i != _midimap.end (); ++i) {
191                 delete i->second;
192         }
193         for (uint8_t id = 0; id < 8; ++id) {
194                 delete chanstrip[id];
195         }
196         _midimap_strip.clear ();
197         _ctrlmap.clear ();
198         _midimap.clear ();
199 }
200
201 bool
202 FP8Controls::button_name_to_enum (std::string const& n, ButtonId& id) const
203 {
204         std::map<std::string, ButtonId>::const_iterator i = _user_str_to_enum.find (n);
205         if (i == _user_str_to_enum.end()) {
206                 return false;
207         }
208         id = i->second;
209         return true;
210 }
211
212 bool
213 FP8Controls::button_enum_to_name (ButtonId id, std::string& n) const
214 {
215         std::map<ButtonId, std::string>::const_iterator i = _user_enum_to_str.find (id);
216         if (i == _user_enum_to_str.end()) {
217                 return false;
218         }
219         n = i->second;
220         return true;
221 }
222
223 void
224 FP8Controls::initialize ()
225 {
226         FP8ButtonInterface::force_change = true;
227         /* set RGB colors */
228         button (BtnUndo).set_color (0x00ff00ff);
229         button (BtnRedo).set_color (0x00ff00ff);
230
231         button (BtnAOff).set_color (0xffffffff);
232         button (BtnATrim).set_color (0x000030ff);
233         button (BtnARead).set_color (0x00ff00ff);
234         button (BtnAWrite).set_color (0xff0000ff);
235         button (BtnATouch).set_color (0xff8800ff);
236         button (BtnALatch).set_color (0xffff00ff);
237
238         button (BtnUser1).set_color (0x0000ffff);
239         button (BtnUser2).set_color (0x0000ffff);
240         button (BtnUser3).set_color (0x0000ffff);
241
242         button (BtnBypass).set_color (0x888888ff);
243         button (BtnBypassAll).set_color (0xffffffff);
244
245         button (BtnMacro).set_color (0x888888ff);
246         button (BtnOpen).set_color (0xffffffff);
247
248         button (BtnLink).set_color (0x888888ff);
249         button (BtnLock).set_color (0xffffffff);
250
251         button (BtnMAudio).set_color (0x0000ffff);
252         button (BtnMVI).set_color (0x0000ffff);
253         button (BtnMBus).set_color (0x0000ffff);
254         button (BtnMVCA).set_color (0x0000ffff);
255         button (BtnMAll).set_color (0x0000ffff);
256
257         button (BtnMInputs).set_color (0x0000ffff);
258         button (BtnMMIDI).set_color (0x0000ffff);
259         button (BtnMOutputs).set_color (0x0000ffff);
260         button (BtnMFX).set_color (0x0000ffff);
261         button (BtnMUser).set_color (0x0000ffff);
262
263         for (uint8_t id = 0; id < 8; ++id) {
264                 chanstrip[id]->initialize ();
265         }
266
267         /* initally turn all lights off */
268         all_lights_off ();
269
270         /* default modes */
271         button (BtnMaster).set_active (true);
272         button (BtnTrack).set_active (true);
273         button (BtnMAll).set_active (true);
274         button (BtnTimecode).set_active (_display_timecode);
275
276         FP8ButtonInterface::force_change = false;
277 }
278
279 void
280 FP8Controls::all_lights_off () const
281 {
282         for (CtrlButtonMap::const_iterator i = _ctrlmap.begin (); i != _ctrlmap.end (); ++i) {
283                 i->second->set_active (false);
284         }
285 }
286
287 FP8ButtonInterface&
288 FP8Controls::button (ButtonId id)
289 {
290         CtrlButtonMap::const_iterator i = _ctrlmap.find (id);
291         if (i == _ctrlmap.end()) {
292                 assert (0);
293                 return _dummy_button;
294         }
295         return *(i->second);
296 }
297
298 FP8Strip&
299 FP8Controls::strip (uint8_t id)
300 {
301         assert (id < 8);
302         return *chanstrip[id];
303 }
304
305 /* *****************************************************************************
306  * Delegate MIDI events
307  */
308
309 bool
310 FP8Controls::midi_event (uint8_t id, uint8_t val)
311 {
312         MidiButtonMap::const_iterator i;
313
314         i = _midimap_strip.find (id);
315         if (i != _midimap_strip.end()) {
316                 return i->second->midi_event (val > 0x40);
317         }
318
319         i = _midimap.find (id);
320         if (i != _midimap.end()) {
321                 return i->second->midi_event (val > 0x40);
322         }
323         return false;
324 }
325
326 bool
327 FP8Controls::midi_touch (uint8_t id, uint8_t val)
328 {
329         assert (id < 8);
330         return chanstrip[id]->midi_touch (val > 0x40);
331 }
332
333 bool
334 FP8Controls::midi_fader (uint8_t id, unsigned short val)
335 {
336         assert (id < 8);
337         return chanstrip[id]->midi_fader ((val >> 4) / 1023.f);
338 }
339
340 /* *****************************************************************************
341  * Internal Model + View for Modes
342  */
343
344 void
345 FP8Controls::set_nav_mode (NavigationMode m)
346 {
347         if (_navmode == m) {
348                 return;
349         }
350         // TODO add special-cases:
351         // - master/monitor (blink when button is held + monitor section present)
352         // - "click" hold -> encoder sets click volume, encoder-press toggle rec-only-metro
353         button (BtnChannel).set_active (m == NavChannel);
354         button (BtnZoom).set_active (m == NavZoom);
355         button (BtnScroll).set_active (m == NavScroll);
356         button (BtnBank).set_active (m == NavBank);
357         button (BtnMaster).set_active (m == NavMaster);
358         button (BtnSection).set_active (m == NavSection);
359         button (BtnMarker).set_active (m == NavMarker);
360         _navmode = m;
361 }
362
363 void
364 FP8Controls::set_fader_mode (FaderMode m)
365 {
366         if (_fadermode == m) {
367                 if (m == ModePlugins || m == ModeSend) {
368                         /* "Edit Plugins" while editing Plugin-params, returns back
369                          * to plugin selection.
370                          * "Sends" button banks through sends.
371                          */
372                         FaderModeChanged ();
373                 }
374                 return;
375         }
376         // set lights
377         button (BtnTrack).set_active (m == ModeTrack);
378         button (BtnPlugins).set_active (m == ModePlugins);
379         button (BtnSend).set_active (m == ModeSend);
380         button (BtnPan).set_active (m == ModePan);
381         _fadermode = m;
382         FaderModeChanged ();
383 }
384
385 void
386 FP8Controls::set_mix_mode (MixMode m)
387 {
388         if (_mixmode == m) {
389                 if (m == MixUser || m == MixInputs) {
390                         /* always re-assign:
391                          *  - MixUser: depends on selection
392                          *  - MixInputs: depends on rec-arm
393                          */
394                         MixModeChanged ();
395                 }
396                 return;
397         }
398         button (BtnMAudio).set_active (m == MixAudio);
399         button (BtnMVI).set_active (m == MixInstrument);
400         button (BtnMBus).set_active (m == MixBus);
401         button (BtnMVCA).set_active (m == MixVCA);
402         button (BtnMAll).set_active (m == MixAll);
403         button (BtnMInputs).set_active (m == MixInputs);
404         button (BtnMMIDI).set_active (m == MixMIDI);
405         button (BtnMOutputs).set_active (m == MixOutputs);
406         button (BtnMFX).set_active (m == MixFX);
407         button (BtnMUser).set_active (m == MixUser);
408
409         _mixmode = m;
410         MixModeChanged ();
411 }
412
413 void
414 FP8Controls::toggle_timecode ()
415 {
416         _display_timecode = !_display_timecode;
417         button (BtnTimecode).set_active (_display_timecode);
418 }