1 /* FaderPort8 Button Interface
3 * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #ifndef _ardour_surfaces_fp8button_h_
21 #define _ardour_surfaces_fp8button_h_
25 #include "pbd/base_ui.h"
26 #include "pbd/signals.h"
30 namespace ArdourSurface { namespace FP_NAMESPACE {
32 /* virtual base-class and interface */
33 class FP8ButtonInterface
36 FP8ButtonInterface () {}
37 virtual ~FP8ButtonInterface () {}
40 PBD::Signal0<void> pressed;
41 PBD::Signal0<void> released;
43 virtual bool is_pressed () const { return false; }
44 virtual bool is_active () const { return false; }
46 virtual void ignore_release () {}
48 /* internal API - called from midi thread,
49 * user pressed/released button the device
51 virtual bool midi_event (bool) = 0;
53 /* internal API - called from surface thread
54 * set Light on the button
56 virtual void set_active (bool a) = 0;
57 virtual void set_color (uint32_t rgba) {}
58 virtual void set_blinking (bool) {}
60 static bool force_change; // used during init
63 /* ****************************************************************************
67 class FP8DummyButton : public FP8ButtonInterface
70 virtual void set_active (bool a) {}
71 virtual bool midi_event (bool) { return false; }
75 /* common implementation */
76 class FP8ButtonBase : public FP8ButtonInterface
79 FP8ButtonBase (FP8Base& b)
83 , _ignore_release (false)
88 bool is_pressed () const { return _pressed; }
89 bool is_active () const { return _active; }
91 virtual bool midi_event (bool a)
98 pressed (); /* EMIT SIGNAL */
100 if (_ignore_release) {
101 _ignore_release = false;
103 released (); /* EMIT SIGNAL */
109 virtual void ignore_release () {
111 _ignore_release = true;
115 bool blinking () const { return _blinking; }
117 void set_blinking (bool yes) {
118 if (yes && !_blinking) {
120 _base.BlinkIt.connect_same_thread (_blink_connection, boost::bind (&FP8ButtonBase::blink, this, _1));
121 } else if (!yes && _blinking) {
122 _blink_connection.disconnect ();
132 bool _ignore_release;
134 virtual void blink (bool onoff) = 0;
137 PBD::ScopedConnection _blink_connection;
141 /* A basic LED or RGB button, not shift sensitive */
142 class FP8Button : public FP8ButtonBase
145 FP8Button (FP8Base& b, uint8_t id, bool color = false)
151 virtual void set_active (bool a)
153 if (_active == a && !force_change) {
157 _base.tx_midi3 (0x90, _midi_id, a ? 0x7f : 0x00);
160 void set_color (uint32_t rgba)
162 if (!_has_color || _rgba == rgba) {
166 _base.tx_midi3 (0x91, _midi_id, (_rgba >> 25) & 0x7f);
167 _base.tx_midi3 (0x92, _midi_id, (_rgba >> 17) & 0x7f);
168 _base.tx_midi3 (0x93, _midi_id, (_rgba >> 9) & 0x7f);
172 void blink (bool onoff)
174 if (!_active) { return; }
175 _base.tx_midi3 (0x90, _midi_id, onoff ? 0x7f : 0x00);
178 uint8_t _midi_id; // MIDI-note
182 /* footswitch and encoder-press buttons */
183 class FP8ReadOnlyButton : public FP8Button
186 FP8ReadOnlyButton (FP8Base& b, uint8_t id, bool color = false)
187 : FP8Button (b, id, color)
190 void set_active (bool) { }
193 /* virtual button. used for shift toggle. */
194 class ShadowButton : public FP8ButtonBase
197 ShadowButton (FP8Base& b)
201 PBD::Signal1<void, bool> ActiveChanged;
202 PBD::Signal0<void> ColourChanged;
204 uint32_t color () const { return _rgba; }
206 bool midi_event (bool a)
212 bool set_pressed (bool a)
214 return FP8ButtonBase::midi_event (a);
217 void set_active (bool a)
219 if (_active == a && !force_change) {
223 ActiveChanged (a); /* EMIT SIGNAL */
226 void set_color (uint32_t rgba)
236 void blink (bool onoff) {
237 if (!_active) { return; }
238 ActiveChanged (onoff);
242 /* Wraps 2 buttons with the same physical MIDI ID */
243 class FP8DualButton : public FP8ButtonInterface
246 FP8DualButton (FP8Base& b, uint8_t id, bool color = false)
255 _b0.ActiveChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::active_changed, this, false, _1));
256 _b1.ActiveChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::active_changed, this, true, _1));
258 _b0.ColourChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::colour_changed, this, false));
259 _b1.ColourChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::colour_changed, this, true));
263 bool midi_event (bool a) {
264 return (_shift ? _b1 : _b0).set_pressed (a);
267 void set_active (bool a) {
268 /* This button is never directly used
269 * by the libardour side API.
274 void active_changed (bool s, bool a) {
278 _base.tx_midi3 (0x90, _midi_id, a ? 0x7f : 0x00);
281 void colour_changed (bool s) {
282 if (s != _shift || !_has_color) {
285 uint32_t rgba = (_shift ? _b1 : _b0).color ();
290 _base.tx_midi3 (0x91, _midi_id, (rgba >> 25) & 0x7f);
291 _base.tx_midi3 (0x92, _midi_id, (rgba >> 17) & 0x7f);
292 _base.tx_midi3 (0x93, _midi_id, (rgba >> 9) & 0x7f);
295 FP8ButtonInterface* button () { return &_b0; }
296 FP8ButtonInterface* button_shift () { return &_b1; }
301 virtual void connect_toggle () = 0;
303 void shift_changed (bool shift) {
304 if (_shift == shift) {
307 (_shift ? _b1 : _b0).set_pressed (false);
309 active_changed (_shift, (_shift ? _b1 : _b0).is_active());
310 colour_changed (_shift);
316 uint8_t _midi_id; // MIDI-note
320 PBD::ScopedConnectionList _button_connections;
323 class FP8ShiftSensitiveButton : public FP8DualButton
326 FP8ShiftSensitiveButton (FP8Base& b, uint8_t id, bool color = false)
327 :FP8DualButton (b, id, color)
333 void connect_toggle ()
335 _base.ShiftButtonChange.connect_same_thread (_shift_connection, boost::bind (&FP8ShiftSensitiveButton::shift_changed, this, _1));
339 PBD::ScopedConnection _shift_connection;
342 class FP8ARMSensitiveButton : public FP8DualButton
345 FP8ARMSensitiveButton (FP8Base& b, uint8_t id, bool color = false)
346 :FP8DualButton (b, id, color)
352 void connect_toggle ()
354 _base.ARMButtonChange.connect_same_thread (_arm_connection, boost::bind (&FP8ARMSensitiveButton::shift_changed, this, _1));
358 PBD::ScopedConnection _arm_connection;
362 // short press: activate in press, deactivate on release,
363 // long press + hold, activate on press, de-activate directly on release
364 // e.g. mute/solo press + hold => changed()
365 class FP8MomentaryButton : public FP8ButtonBase
368 FP8MomentaryButton (FP8Base& b, uint8_t id)
373 ~FP8MomentaryButton () {
374 _hold_connection.disconnect ();
377 PBD::Signal1<void, bool> StateChange;
379 void set_active (bool a)
381 if (_active == a && !force_change) {
385 _base.tx_midi3 (0x90, _midi_id, a ? 0x7f : 0x00);
390 _was_active_on_press = false;
391 _hold_connection.disconnect ();
394 void ignore_release () { }
396 bool midi_event (bool a)
405 _was_active_on_press = _active;
410 StateChange (true); /* EMIT SIGNAL */
411 Glib::RefPtr<Glib::TimeoutSource> hold_timer =
412 Glib::TimeoutSource::create (500);
413 hold_timer->attach (fp8_loop()->get_context());
414 _hold_connection = hold_timer->connect (sigc::mem_fun (*this, &FP8MomentaryButton::hold_timeout));
415 } else if (!a && _was_active_on_press) {
416 _hold_connection.disconnect ();
418 StateChange (false); /* EMIT SIGNAL */
419 } else if (!a && _momentaty) {
420 _hold_connection.disconnect ();
422 StateChange (false); /* EMIT SIGNAL */
428 void blink (bool onoff)
431 _base.tx_midi3 (0x90, _midi_id, _active ? 0x7f : 0x00);
434 _base.tx_midi3 (0x90, _midi_id, onoff ? 0x7f : 0x00);
437 uint8_t _midi_id; // MIDI-note
439 bool _was_active_on_press;
447 sigc::connection _hold_connection;
450 /* an auto-repeat button.
451 * press + hold emits continuous "press" events.
453 class FP8RepeatButton : public FP8Button
456 FP8RepeatButton (FP8Base& b, uint8_t id, bool color = false)
457 : FP8Button (b, id, color)
466 bool midi_event (bool a)
468 bool rv = FP8Button::midi_event (a);
477 _press_timeout_connection.disconnect ();
485 Glib::RefPtr<Glib::TimeoutSource> press_timer =
486 Glib::TimeoutSource::create (100);
487 press_timer->attach (fp8_loop()->get_context());
488 _press_timeout_connection = press_timer->connect (sigc::mem_fun (*this, &FP8RepeatButton::repeat_press));
505 sigc::connection _press_timeout_connection;
509 #endif /* _ardour_surfaces_fp8button_h_ */