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