reinstate wiimote support, thanks to work by jannis pohlmann
[ardour.git] / libs / surfaces / wiimote / wiimote.cc
1 /*
2     Copyright (C) 2009-2013 Paul Davis
3     Authors: Sampo Savolainen, Jannis Pohlmann
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
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.
14
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <iostream>
22
23 #include "pbd/compose.h"
24 #include "pbd/error.h"
25 #include "ardour/debug.h"
26 #include "ardour/session.h"
27 #include "i18n.h"
28
29 #include "wiimote.h"
30
31 using namespace ARDOUR;
32 using namespace PBD;
33 using namespace std;
34
35 #include "pbd/abstract_ui.cc" // instantiate template
36
37 void wiimote_control_protocol_mesg_callback (cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], timespec *t);
38
39 WiimoteControlProtocol::WiimoteControlProtocol (Session& session)
40         : ControlProtocol (session, X_("Wiimote"))
41         , AbstractUI<WiimoteControlUIRequest> ("wiimote")
42         , wiimote (0)
43         , idle_source (0)
44         , button_state (0)
45         , callback_thread_registered (false)
46 {
47 }
48
49 WiimoteControlProtocol::~WiimoteControlProtocol ()
50 {
51         stop ();
52 }
53
54 bool
55 WiimoteControlProtocol::probe ()
56 {
57         return true;
58 }
59
60 int
61 WiimoteControlProtocol::set_active (bool yn)
62 {
63         int result;
64
65         DEBUG_TRACE (DEBUG::WiimoteControl, string_compose ("WiimoteControlProtocol::set_active init with yn: '%1'\n", yn));
66
67         /* do nothing if the active state is not changing */
68         if (yn == _active) {
69                 return 0;
70         }
71
72         if (yn) {
73                 /* activate Wiimote control surface */
74                 result = start ();
75         } else {
76                 /* deactivate Wiimote control surface */
77                 result = stop ();
78         }
79
80         /* remember new active state */
81         _active = yn;
82
83         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::set_active done\n");
84
85         return result;
86 }
87
88 XMLNode&
89 WiimoteControlProtocol::get_state ()
90 {
91         XMLNode *node = new XMLNode ("Protocol");
92         node->add_property (X_("name"), ARDOUR::ControlProtocol::_name);
93         node->add_property (X_("feedback"), "0");
94         return *node;
95 }
96
97 int
98 WiimoteControlProtocol::set_state (const XMLNode&, int)
99 {
100         return 0;
101 }
102
103 void
104 WiimoteControlProtocol::do_request (WiimoteControlUIRequest* req)
105 {
106         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::do_request init\n");
107
108         if (req->type == CallSlot) {
109                 call_slot (MISSING_INVALIDATOR, req->the_slot);
110         } else if (req->type == Quit) {
111                 stop ();
112         }
113
114         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::do_request done\n");
115 }
116
117 int
118 WiimoteControlProtocol::start ()
119 {
120         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::start init\n");
121
122         // update LEDs whenever the transport or recording state changes
123         session->TransportStateChange.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&WiimoteControlProtocol::update_led_state, this), this);
124         session->RecordStateChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&WiimoteControlProtocol::update_led_state, this), this);
125
126         // start the Wiimote control UI; it will run in its own thread context
127         BaseUI::run ();
128
129         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::start done\n");
130
131         return 0;
132 }
133
134 int
135 WiimoteControlProtocol::stop ()
136 {
137         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::stop init\n");
138
139         // stop wiimote discovery, just in case
140         stop_wiimote_discovery ();
141
142         // close and reset the wiimote handle
143         if (wiimote) {
144                 cwiid_close (wiimote);
145                 wiimote = 0;
146                 callback_thread_registered = false;
147         }
148
149         // stop the Wiimote control UI
150         BaseUI::quit ();
151
152         // no longer update the LEDs
153         session_connections.drop_connections ();
154
155         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::stop done\n");
156
157         return 0;
158 }
159
160 void
161 WiimoteControlProtocol::thread_init ()
162 {
163         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::thread_init init\n");
164
165         pthread_set_name (X_("wiimote"));
166
167         // allow to make requests to the GUI and RT thread(s)
168         PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self (), X_("wiimote"), 2048);
169         BasicUI::register_thread ("wiimote");
170
171         // connect a Wiimote
172         start_wiimote_discovery ();
173
174         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::thread_init done\n");
175 }
176
177 void
178 WiimoteControlProtocol::start_wiimote_discovery ()
179 {
180         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::start_wiimote_discovery init\n");
181
182         // connect to the Wiimote using an idle source
183         Glib::RefPtr<Glib::IdleSource> source = Glib::IdleSource::create ();
184         source->connect (sigc::mem_fun (*this, &WiimoteControlProtocol::connect_idle));
185         source->attach (_main_loop->get_context ());
186
187         // grab a reference on the underlying idle source to keep it around
188         idle_source = source->gobj ();
189         g_source_ref (idle_source);
190
191         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::start_wiimote_discovery done\n");
192 }
193
194 void
195 WiimoteControlProtocol::stop_wiimote_discovery ()
196 {
197         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::stop_wiimote_discovery init\n");
198
199         if (idle_source) {
200                 g_source_unref (idle_source);
201                 idle_source = 0;
202         }
203
204         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::stop_wiimote_discovery done\n");
205 }
206
207 bool
208 WiimoteControlProtocol::connect_idle ()
209 {
210         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::connect_idle init\n");
211
212         bool retry = true;
213
214         if (connect_wiimote ()) {
215                 stop_wiimote_discovery ();
216                 retry = false;
217         }
218
219         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::connect_idle done\n");
220
221         return retry;
222 }
223
224 bool
225 WiimoteControlProtocol::connect_wiimote ()
226 {
227         // abort the discovery and do nothing else if we already have a Wiimote
228         if (wiimote) {
229                 return true;
230         }
231
232         bool success = true;
233
234         // if we don't have a Wiimote yet, try to discover it; if that
235         // fails, wait for a short period of time and try again
236         if (!wiimote) {
237                 cerr << "Wiimote: Not discovered yet, press 1+2 to connect" << endl;
238
239                 bdaddr_t bdaddr = {{ 0, 0, 0, 0, 0, 0 }};
240                 wiimote = cwiid_open (&bdaddr, 0);
241                 callback_thread_registered = false;
242                 if (!wiimote) {
243                         success = false;
244                 } else {
245                         // a Wiimote was discovered
246                         cerr << "Wiimote: Connected successfully" << endl;
247
248                         // attach the WiimoteControlProtocol object to the Wiimote handle
249                         if (cwiid_set_data (wiimote, this)) {
250                                 cerr << "Wiimote: Failed to attach control protocol" << endl;
251                                 success = false;
252                         }
253
254                         // clear the last button state to start processing events cleanly
255                         button_state = 0;
256                 }
257         }
258
259         // enable message based communication with the Wiimote
260         if (success && cwiid_enable (wiimote, CWIID_FLAG_MESG_IFC)) {
261                 cerr << "Wiimote: Failed to enable message based communication" << endl;
262                 success = false;
263         }
264
265         // enable button events to be received from the Wiimote
266         if (success && cwiid_command (wiimote, CWIID_CMD_RPT_MODE, CWIID_RPT_BTN)) {
267                 cerr << "Wiimote: Failed to enable button events" << endl;
268                 success = false;
269         }
270
271         // receive an event for every single button pressed, not just when
272         // a different button was pressed than before
273         if (success && cwiid_enable (wiimote, CWIID_FLAG_REPEAT_BTN)) {
274                 cerr << "Wiimote: Failed to enable repeated button events" << endl;
275                 success = false;
276         }
277
278         // be notified of new input events
279         if (success && cwiid_set_mesg_callback (wiimote, wiimote_control_protocol_mesg_callback)) {
280         }
281
282         // reset Wiimote handle if the configuration failed
283         if (!success && wiimote) {
284                 cwiid_close (wiimote);
285                 wiimote = 0;
286                 callback_thread_registered = false;
287         }
288
289         return success;
290 }
291
292 void
293 WiimoteControlProtocol::update_led_state ()
294 {
295         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state init\n");
296
297         uint8_t state = 0;
298
299         // do nothing if we do not have a Wiimote
300         if (!wiimote) {
301                 DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state no wiimote connected\n");
302                 return;
303         }
304
305         // enable LED1 if Ardour is playing
306         if (session->transport_rolling ()) {
307                 DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state playing, activate LED1\n");
308                 state |= CWIID_LED1_ON;
309         }
310
311         // enable LED4 if Ardour is recording
312         if (session->actively_recording ()) {
313                 DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state recording, activate LED4\n");
314                 state |= CWIID_LED4_ON;
315         }
316
317         // apply the LED state
318         cwiid_set_led (wiimote, state);
319
320         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state done\n");
321 }
322
323 void
324 WiimoteControlProtocol::wiimote_callback (int mesg_count, union cwiid_mesg mesg[])
325 {
326         // register the cwiid callback thread if that hasn't happened yet
327         if (!callback_thread_registered) {
328                 BasicUI::register_thread ("wiimote callback");
329                 callback_thread_registered = true;
330         }
331
332         for (int i = 0; i < mesg_count; i++) {
333                 // restart Wiimote discovery when receiving errors
334                 if (mesg[i].type == CWIID_MESG_ERROR) {
335                         cerr << "Wiimote: disconnected" << endl;
336                         cwiid_close (wiimote);
337                         wiimote = 0;
338                         callback_thread_registered = false;
339                         start_wiimote_discovery ();
340                         return;
341                 }
342
343                 // skip non-button events
344                 if (mesg[i].type != CWIID_MESG_BTN) {
345                         continue;
346                 }
347
348                 // drop buttons from the event that were already pressed before
349                 uint16_t b = mesg[i].btn_mesg.buttons & ~button_state;
350
351                 // remember new button state
352                 button_state = mesg[i].btn_mesg.buttons;
353
354                 if (button_state & CWIID_BTN_B) {
355                         // B + A = abort recording and jump back
356                         if (b & CWIID_BTN_A) {
357                                 access_action ("Transport/ToggleRollForgetCapture");
358                         }
359
360                         // B + left = move playhead to previous region boundary
361                         if (b & CWIID_BTN_LEFT) {
362                                 access_action ("Editor/playhead-to-previous-region-boundary");
363                         }
364
365                         // B + right = move playhead to next region boundary
366                         if (b & CWIID_BTN_RIGHT) {
367                                 access_action ("Editor/playhead-to-next-region-boundary");
368                         }
369
370                         // B + up = move playhead to next marker
371                         if (b & CWIID_BTN_UP) {
372                                 next_marker ();
373                         }
374
375                         // B + down = move playhead to prev marker
376                         if (b & CWIID_BTN_DOWN) {
377                                 prev_marker ();
378                         }
379
380                         // B + Home = add marker at playhead
381                         if (b & CWIID_BTN_HOME) {
382                                 access_action ("Editor/add-location-from-playhead");
383                         }
384
385                         // B + minus = move playhead to the start
386                         if (b & CWIID_BTN_MINUS) {
387                                 access_action ("Transport/GotoStart");
388                         }
389
390                         // B + plus = move playhead to the end
391                         if (b & CWIID_BTN_PLUS) {
392                                 access_action ("Transport/GotoEnd");
393                         }
394                 } else {
395                         // A = toggle playback
396                         if (b & CWIID_BTN_A) {
397                                 access_action ("Transport/ToggleRoll");
398                         }
399
400                         // 1 = toggle recording on the current track
401                         if (b & CWIID_BTN_1) {
402                                 access_action ("Editor/track-record-enable-toggle");
403                         }
404
405                         // 2 = enable recording in general
406                         if (b & CWIID_BTN_2) {
407                                 rec_enable_toggle ();
408                         }
409
410                         // left = move playhead back a bit
411                         if (b & CWIID_BTN_LEFT) {
412                                 access_action ("Editor/nudge-playhead-backward");
413                         }
414
415                         // right = move playhead forward a bit
416                         if (b & CWIID_BTN_RIGHT) {
417                                 access_action ("Editor/nudge-playhead-forward");
418                         }
419
420                         // up = select previous track
421                         if (b & CWIID_BTN_UP) {
422                                 access_action ("Editor/select-prev-route");
423                         }
424
425                         // down = select next track
426                         if (b & CWIID_BTN_DOWN) {
427                                 access_action ("Editor/select-next-route");
428                         }
429
430                         // + = zoom in
431                         if (b & CWIID_BTN_PLUS) {
432                                 access_action ("Editor/temporal-zoom-in");
433                         }
434
435                         // - = zoom out
436                         if (b & CWIID_BTN_MINUS) {
437                                 access_action ("Editor/temporal-zoom-out");
438                         }
439
440                         // home = no-op
441                         if (b & CWIID_BTN_HOME) {
442                                 access_action ("Editor/playhead-to-edit");
443                         }
444                 }
445         }
446 }
447
448 void
449 wiimote_control_protocol_mesg_callback (cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], timespec *)
450 {
451         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::mesg_callback init\n");
452
453         WiimoteControlProtocol *protocol = (WiimoteControlProtocol *)cwiid_get_data (wiimote);
454
455         if (protocol) {
456                 protocol->wiimote_callback (mesg_count, mesg);
457         }
458
459         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::mesg_callback done\n");
460 }