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