/[soapbox]/misc.wiiremote/trunk/wiimote.h
ViewVC logotype

Contents of /misc.wiiremote/trunk/wiimote.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3118 - (show annotations) (download)
Sat May 14 12:26:33 2011 UTC (11 years, 1 month ago) by tadas
File MIME type: text/plain
File size: 21109 byte(s)
Skeleton of the whole thing
1 // _______________________________________________________________________________
2 //
3 // - WiiYourself! - native C++ Wiimote library v1.15
4 // (c) gl.tter 2007-10 - http://gl.tter.org
5 //
6 // see License.txt for conditions of use. see History.txt for change log.
7 // _______________________________________________________________________________
8 //
9 // wiimote.h (tab = 4 spaces)
10
11 #ifdef _MSC_VER // VC
12 # pragma once
13 #endif
14
15 #ifndef _WIIMOTE_H
16 # define _WIIMOTE_H
17
18 #define WIN32_LEAN_AND_MEAN
19 #include <windows.h>
20 #include <tchar.h> // auto Unicode/Ansi support
21 #include <queue> // for HID write method
22 #include <list> // for state recording
23
24 #ifndef QWORD
25 typedef unsigned __int64 QWORD;
26 #endif
27
28 #ifdef _MSC_VER // VC-specific: _DEBUG build only _ASSERT() sanity checks
29 # include <crtdbg.h>
30 #elif defined(__MINGW32__) // define NDEBUG to disable assert
31 # include <assert.h>
32 # define _ASSERT assert
33 #else
34 # define _ASSERT(x) ((void)0) // (add your compiler's implementation if you like)
35 #endif
36
37 #ifdef SWIGWRAPPER // Python Wrapper
38 #include "Python/wiimote_state.i"
39 #else
40 #include "wiimote_state.h"
41 #endif
42
43 // configs:
44 //#define USE_DYNAMIC_HIDQUEUE // deprecated
45
46 // we request periodic status report updates to refresh the battery level
47 // and to detect connection loss (through failed writes)
48 #define REQUEST_STATUS_EVERY_MS 1000
49 #define DETECT_MPLUS_EVERY_MS 1000
50 #define DETECT_MPLUS_COUNT 1 // # of tries in quick succession
51
52 // all threads (read/parse, audio streaming, async rumble...) use this priority
53 #define WORKER_THREAD_PRIORITY THREAD_PRIORITY_HIGHEST
54
55 // internals
56 #define WIIYOURSELF_VERSION_MAJOR 1
57 #define WIIYOURSELF_VERSION_MINOR1 1
58 #define WIIYOURSELF_VERSION_MINOR2 5
59 //#define WIIYOURSELF_VERSION_BETA // not defined for non-beta releases
60 #define WIIYOURSELF_VERSION_STR _T("1.15")
61
62 // array sizes
63 #define TOTAL_BUTTON_BITS 16 // Number of bits for (Classic)ButtonNameFromBit[]
64 #define TOTAL_FREQUENCIES 10 // Number of frequencies (see speaker_freq[])
65
66 // clarity
67 typedef HANDLE EVENT;
68
69
70 // state data changes can be signalled to the app via a callback. Set the wiimote
71 // object's 'ChangedCallback' any time to enable them, or alternatively inherit
72 // from the wiimote object and override the ChangedNotifier() virtual method.
73
74 // of flags indicating which state has changed since the last callback.
75 typedef void (*state_changed_callback) (class wiimote &owner,
76 state_change_flags changed,
77 const wiimote_state &new_state);
78
79 // internals
80 typedef BOOLEAN (__stdcall *hidwrite_ptr)(HANDLE HidDeviceObject,
81 PVOID ReportBuffer,
82 ULONG ReportBufferLength);
83
84 // (global due to Python wrapper)
85 struct wiimote_state_event {
86 DWORD time_ms; // system timestamp in milliseconds
87 wiimote_state state;
88 };
89
90 // wiimote class - connects and manages a wiimote and its optional extensions
91 // (Nunchuk/Classic Controller), and exposes their state
92 class wiimote : public wiimote_state
93 {
94 public:
95 wiimote ();
96 virtual ~wiimote ();
97
98 public:
99 // these can be used to identify Connect()ed wiimote objects (if both
100 // are unconnected they will pass the compare as their handles are invalid)
101 inline bool operator == (const wiimote& remote)
102 { return Handle == remote.Handle; }
103 inline bool operator != (const wiimote& remote)
104 { return Handle != remote.Handle; }
105
106 // wiimote data input mode (use with SetReportType())
107 // (only enable what you need to save battery power)
108 enum input_report
109 {
110 // combinations if buttons/acceleration/IR/Extension data
111 IN_BUTTONS = 0x30,
112 IN_BUTTONS_ACCEL = 0x31,
113 IN_BUTTONS_ACCEL_IR = 0x33, // reports IR EXTENDED data (dot sizes)
114 IN_BUTTONS_ACCEL_EXT = 0x35,
115 IN_BUTTONS_ACCEL_IR_EXT = 0x37, // reports IR BASIC data (no dot sizes)
116 IN_BUTTONS_BALANCE_BOARD = 0x32, // must use this for the balance board
117 };
118 // string versions
119 static const TCHAR* ReportTypeName [];
120
121
122 public: // convenience accessors:
123 inline bool IsConnected () const { return bStatusReceived; }
124 // if IsConnected() unexpectedly returns false, connection was probably lost
125 inline bool ConnectionLost () const { return bConnectionLost; }
126 inline bool IsBalanceBoard () const { return (Internal.bExtension &&
127 (Internal.ExtensionType==wiimote_state::BALANCE_BOARD)); }
128 inline bool NunchukConnected () const { return (Internal.bExtension &&
129 (Internal.ExtensionType==wiimote_state::NUNCHUK)); }
130 inline bool ClassicConnected () const { return (Internal.bExtension &&
131 (Internal.ExtensionType==wiimote_state::CLASSIC)); }
132 inline bool MotionPlusConnected () const { return bMotionPlusDetected; }
133 inline bool MotionPlusEnabled () const { return bMotionPlusEnabled; }
134 inline bool MotionPlusHasExtension() const { return bMotionPlusExtension; }
135 inline bool IsPlayingAudio () const { return (Internal.Speaker.Freq &&
136 Internal.Speaker.Volume); }
137 inline bool IsPlayingSample () const { return IsPlayingAudio() &&
138 (CurrentSample != NULL); }
139 inline bool IsUsingHIDwrites () const { return bUseHIDwrite; }
140 inline bool IsRecordingState () const { return Recording.bEnabled; }
141
142 static inline unsigned TotalConnected() { return _TotalConnected; }
143
144
145 public: // data
146 QWORD UniqueID; // constructed from device-specific calibration info.
147 // Note this is not guaranteed to be truly unique
148 // as several devices may contain the same calibration
149 // vluaes - but unique amongst a small number of
150 // devices.
151 #ifdef ID2_FROM_DEVICEPATH
152 QWORD UniqueID2; // (low-reliabilty, left for reference)
153 // constructed from the 'device path' string (as
154 // reported by the OS/stack). This is hopefully
155 // unique as long as the devices remain installed
156 // (or at least paired).
157 #endif
158 // optional callbacks - set these to your own fuctions (if required)
159 state_changed_callback ChangedCallback;
160 // you can avoid unnecessary callback overhead by specifying a mask
161 // of which state changes should trigger them (default is any)
162 state_change_flags CallbackTriggerFlags;
163 // alternatively, inherit from this class and override this virtual function:
164 virtual void ChangedNotifier (state_change_flags changed,
165 const wiimote_state &new_state) {};
166
167 // get the button name from its bit index (some bits are unused)
168 static const TCHAR* ButtonNameFromBit [TOTAL_BUTTON_BITS];
169 static const TCHAR* GetButtonNameFromBit (unsigned index)
170 {
171 _ASSERT(index < TOTAL_BUTTON_BITS);
172 if(index >= TOTAL_BUTTON_BITS)
173 return _T("[invalid index]");
174 return ButtonNameFromBit[index];
175 }
176
177 // same for the Classic Controller
178 static const TCHAR* ClassicButtonNameFromBit [TOTAL_BUTTON_BITS];
179 static const TCHAR* GetClassicButtonNameFromBit (unsigned index)
180 {
181 _ASSERT(index < TOTAL_BUTTON_BITS);
182 if(index >= TOTAL_BUTTON_BITS)
183 return _T("[invalid index]");
184 return ClassicButtonNameFromBit[index];
185 }
186
187 // get the frequency from speaker_freq enum
188 static const unsigned FreqLookup [TOTAL_FREQUENCIES];
189 static const unsigned GetFreqLookup (unsigned index)
190 {
191 _ASSERT(index < TOTAL_FREQUENCIES);
192 if(index >= TOTAL_FREQUENCIES)
193 return 0;
194 return FreqLookup[index];
195 }
196
197 public: // methods
198 // call Connect() first - returns true if wiimote was found & enabled
199 // - 'wiimote_index' specifies which *installed* (not necessarily
200 // *connected*) wiimote should be tried (1 = first, 2 = 2nd etc).
201 // if you just want the first *available* wiimote that isn't already
202 // in use, pass in FIRST_AVAILABLE (default).
203 // - 'force_hidwrites' forces HID output method (it's auto-selected
204 // when needed and less efficient, so only force for testing).
205 static const unsigned FIRST_AVAILABLE = 0xffffffff;
206 bool Connect (unsigned wiimote_index = FIRST_AVAILABLE,
207 bool force_hidwrites = false);
208 // disconnect from the controller and stop reading data from it
209 void Disconnect ();
210 // set wiimote reporting mode (call after Connnect())
211 // continous = true forces the wiimote to send constant updates, even when
212 // nothing has changed.
213 // = false only sends data when something has changed (note that
214 // acceleration data will cause frequent updates anyway as it
215 // jitters even when the wiimote is stationary)
216 void SetReportType (input_report type, bool continuous = false);
217
218 // toggle the MotionPlus extension. Call MotionPlusDetected() first to
219 // see if it's attached. Unlike normal extensions, the MotionPlus does
220 // not report itself as one until enabled. Once done, it then replaces
221 // any standard extension attached to it, so be sure to disable it
222 // if you want to read those (it's not currently known of both can
223 // be read simultaneously).
224 bool EnableMotionPlus ();
225 bool DisableMotionPlus ();
226
227 // this is used to remove unwanted 'at rest' offsets, currently only from
228 // the Balance Board. make sure there is no weight on the board before
229 // calling this. it reads the current sensor values and then removes them
230 // offsets from all subsequent KG and LB state values (the 'raw' values
231 // are never modified).
232 void CalibrateAtRest ();
233 // NOTE: the library automatically calls this when the first weight values
234 // come in after Connect()ion, but if the device wasn't at rest at
235 // the time the app can call it again later.
236
237 // to read the state via polling (reading the public state data direct from
238 // the wiimote object) you must call RefreshState() at the top of every pass.
239 // returns a combination of flags to indicate which state (if any) has
240 // changed since the last call.
241 state_change_flags RefreshState ();
242
243 // reset the wiimote (changes report type to non-continuous buttons-only,
244 // clears LEDs & rumble, mutes & disables speaker)
245 void Reset ();
246 // set/clear the wiimote LEDs
247 void SetLEDs (BYTE led_bits); // bits 0-3 are valid
248 // set/clear rumble
249 void SetRumble (bool on);
250 // alternative - rumble for a fixed amount of time (asynchronous)
251 void RumbleForAsync (unsigned milliseconds);
252
253 // *experimental* speaker support:
254 bool MuteSpeaker (bool on);
255 bool EnableSpeaker (bool on);
256 bool PlaySquareWave (speaker_freq freq, BYTE volume = 0x40);
257 // note: PlaySample currently streams from the passed-in wiimote_sample -
258 // don't delete it until playback has stopped.
259 bool PlaySample (const wiimote_sample &sample,
260 BYTE volume = 0x40,
261 speaker_freq freq_override = FREQ_NONE);
262
263 // 16bit mono sample loading/conversion to native format:
264 // .wav sample
265 static bool Load16bitMonoSampleWAV (const TCHAR* filepath,
266 wiimote_sample &out);
267 // raw 16bit mono audio data (can be signed or unsigned)
268 static bool Load16BitMonoSampleRAW (const TCHAR* filepath,
269 bool _signed,
270 speaker_freq freq,
271 wiimote_sample &out);
272 // converts a 16bit mono sample array to a wiimote_sample
273 static bool Convert16bitMonoSamples (const short* samples,
274 bool _signed,
275 DWORD length,
276 speaker_freq freq,
277 wiimote_sample &out);
278
279 // state recording - records state snapshots to a 'state_history' supplied
280 // by the caller. states are timestamped and only added
281 // to the list when the specified state changes.
282 #ifndef SWIG // !Python Wrapper
283 typedef wiimote_state_event state_event;
284 #endif
285 typedef std::list<state_event> state_history;
286 static const unsigned UNTIL_STOP = 0xffffffff;
287 // - pass in a 'state_history' list, and don't destroy/change it until
288 // recording is stopped. note the list will be cleared first.
289 // - you can request a specific duration (and use IsRecordingState() to detect
290 // the end), or UNTIL_STOP. StopRecording() can be called either way.
291 // - you can use 'change trigger' to specify specific state changes that will
292 // trigger an insert into the history (others are then ignored).
293 void RecordState (state_history &events_out,
294 unsigned max_time_ms = UNTIL_STOP,
295 state_change_flags change_trigger = CHANGED_ALL);
296 void StopRecording ();
297
298
299 private: // methods
300 // start reading asynchronously from the controller
301 bool BeginAsyncRead ();
302 // request status update (battery level, extension status etc)
303 void RequestStatusReport ();
304 // read address or register from Wiimote asynchronously (the result is
305 // parsed internally whenever it arrives)
306 bool ReadAddress (int address, short size);
307 // write a single BYTE to a wiimote address or register
308 inline void WriteData (int address, BYTE data) { WriteData(address, 1, &data); }
309 // write a BYTE array to a wiimote address or register
310 void WriteData (int address, BYTE size, const BYTE* buff);
311 // callback when data is ready to be processed
312 void OnReadData (DWORD bytes_read);
313 // parse individual reports by type
314 int ParseInput (BYTE* buff);
315 // detects if MotionPlus is attached (it doesn't report as a normal
316 // extesnion until it is enabled)
317 void DetectMotionPlusExtensionAsync ();
318 // initializes an extension when plugged in.
319 void InitializeExtension ();
320 // parses a status report
321 int ParseStatus (BYTE* buff);
322 // parses the buttons
323 int ParseButtons (BYTE* buff);
324 // parses accelerometer data
325 int ParseAccel (BYTE* buff);
326 bool EstimateOrientationFrom(wiimote_state::acceleration &accel);
327 void ApplyJoystickDeadZones (wiimote_state::joystick &joy);
328 // parses IR data from report
329 int ParseIR (BYTE* buff);
330 // parses data from an extension.
331 int ParseExtension (BYTE* buff, unsigned offset);
332 // parses data returned from a read report
333 int ParseReadAddress (BYTE* buff);
334 // reads calibration information stored on Wiimote
335 void ReadCalibration ();
336 float GetBalanceValue(short sensor, short min, short mid, short max);
337 // turns on the IR sensor (the mode must match the reporting mode caps)
338 void EnableIR (wiimote_state::ir::mode mode);
339 // disables the IR sensor
340 void DisableIR ();
341 // writes a report to the Wiimote (NULL = use 'WriteBuff')
342 bool WriteReport (BYTE* buff);
343 bool StartSampleThread ();
344 // returns the rumble BYTE that needs to be sent with reports.
345 inline BYTE GetRumbleBit () const { return Internal.bRumble? 0x01 : 0x00; }
346
347 // static thread funcs:
348 static unsigned __stdcall ReadParseThreadfunc (void* param);
349 static unsigned __stdcall AsyncRumbleThreadfunc (void* param);
350 static unsigned __stdcall SampleStreamThreadfunc(void* param);
351 static unsigned __stdcall HIDwriteThreadfunc (void* param);
352
353 private: // data
354 // wiimote output comands
355 enum output_report
356 {
357 OUT_NONE = 0x00,
358 OUT_LEDs = 0x11,
359 OUT_TYPE = 0x12,
360 OUT_IR = 0x13,
361 OUT_SPEAKER_ENABLE = 0x14,
362 OUT_STATUS = 0x15,
363 OUT_WRITEMEMORY = 0x16,
364 OUT_READMEMORY = 0x17,
365 OUT_SPEAKER_DATA = 0x18,
366 OUT_SPEAKER_MUTE = 0x19,
367 OUT_IR2 = 0x1a,
368 };
369 // input reports used only internally:
370 static const int IN_STATUS = 0x20;
371 static const int IN_READADDRESS = 0x21;
372 // wiimote device IDs:
373 static const int VID = 0x057e; // 'Nintendo'
374 static const int PID = 0x0306; // 'Wiimote'
375 // we could find this out the hard way using HID, but it's 22
376 static const int REPORT_LENGTH = 22;
377 // wiimote registers
378 static const int REGISTER_CALIBRATION = 0x0016;
379 static const int REGISTER_IR = 0x4b00030;
380 static const int REGISTER_IR_SENSITIVITY_1 = 0x4b00000;
381 static const int REGISTER_IR_SENSITIVITY_2 = 0x4b0001a;
382 static const int REGISTER_IR_MODE = 0x4b00033;
383 static const int REGISTER_EXTENSION_INIT1 = 0x4a400f0;
384 static const int REGISTER_EXTENSION_INIT2 = 0x4a400fb;
385 static const int REGISTER_EXTENSION_TYPE = 0x4a400fa;
386 static const int REGISTER_EXTENSION_CALIBRATION = 0x4a40020;
387 static const int REGISTER_BALANCE_CALIBRATION = 0x4a40024;
388 static const int REGISTER_MOTIONPLUS_DETECT = 0x4a600fa;
389 static const int REGISTER_MOTIONPLUS_INIT = 0x4a600f0;
390 static const int REGISTER_MOTIONPLUS_ENABLE = 0x4a600fe;
391
392 HANDLE Handle; // read/write device handle
393 OVERLAPPED Overlapped; // for async Read/WriteFile() IO
394 HANDLE ReadParseThread; // waits for overlapped reads & parses result
395 EVENT DataRead; // signals overlapped read complete
396 bool bUseHIDwrite; // alternative write method (less efficient
397 // but required for some BT stacks (eg. MS')
398 // HidD_SetOutputReport is only supported from XP onwards, so detect &
399 // load it dynamically:
400 static HMODULE HidDLL;
401 static hidwrite_ptr _HidD_SetOutputReport;
402
403 volatile bool bStatusReceived; // for output method detection
404 volatile bool bConnectInProgress; // don't handle extensions until complete
405 volatile bool bInitInProgress; // stop regular requests until complete
406 volatile bool bEnablingMotionPlus; // for special init codepath
407 volatile bool bConnectionLost; // auto-Disconnect()s if set
408 volatile int MotionPlusDetectCount; // waiting for the result
409 volatile bool bMotionPlusDetected;
410 volatile bool bMotionPlusEnabled;
411 volatile bool bMotionPlusExtension;// detected one plugged into MotionPlus
412 volatile bool bCalibrateAtRest; // as soon as the first sensor values // come in after a Connect() call.
413 static unsigned _TotalCreated;
414 static unsigned _TotalConnected;
415 input_report ReportType; // type of data the wiimote delivers
416 // read buffer
417 BYTE ReadBuff [REPORT_LENGTH];
418 // for polling: state is updated on a thread internally, and made only
419 // made public via RefreshState()
420 CRITICAL_SECTION StateLock;
421 wiimote_state Internal;
422 state_change_flags InternalChanged; // state changes since last RefreshState()
423 // periodic status report requests (for battery level and connection loss
424 // detection)
425 DWORD NextStatusTime;
426 DWORD NextMPlusDetectTime;// gap between motion plus detections
427 DWORD MPlusDetectCount; // # of detection tries in quick succesion
428 // async Hidd_WriteReport() thread
429 HANDLE HIDwriteThread;
430 #ifdef USE_DYNAMIC_HIDQUEUE
431 std::queue<BYTE*> HIDwriteQueue;
432 #else
433 // fixed-size queue (to eliminate glitches caused by frequent dynamic memory
434 // allocations)
435 struct hid
436 {
437 hid () : Queue(NULL), ReadIndex(0), WriteIndex(0) {}
438
439 // Increase the static queue size if you get ASSERTs signalling an
440 // overflow (too many reports queued up before being sent by the write
441 // thread). These asserts are harmless though if caused as a result of
442 // loosing the wiimote connection (eg. battery runs out, or wiimote is
443 // unpaired by holding the power button).
444 // Note: MAX_QUEUE_ENTRIES _must_ be a power-of-2, as it
445 // uses index wraparound optimisations.
446 static const unsigned MAX_QUEUE_ENTRIES = 1<<7;
447
448 inline bool IsEmpty() const { return (ReadIndex == WriteIndex); }
449
450 bool Allocate () { // allocate memory (only when needed)
451 _ASSERT(!Queue); if(Queue) return true;
452 ReadIndex = WriteIndex = 0;
453 Queue = new queue_entry[MAX_QUEUE_ENTRIES];
454 _ASSERT(Queue); return (Queue != NULL);
455 }
456 void Deallocate () {
457 if(!Queue) return;
458 delete[] Queue; Queue = NULL;
459 ReadIndex = WriteIndex = 0;
460 }
461
462 struct queue_entry
463 {
464 queue_entry() { memset(Report, 0, sizeof(Report)); }
465
466 BYTE Report [REPORT_LENGTH];
467 } *Queue;
468
469 unsigned ReadIndex, WriteIndex;
470 } HID;
471 #endif
472 CRITICAL_SECTION HIDwriteQueueLock; // queue must be locked before being modified
473
474 // async rumble
475 HANDLE AsyncRumbleThread; // automatically disables rumble if requested
476 volatile DWORD AsyncRumbleTimeout;
477 // orientation estimation
478 unsigned WiimoteNearGUpdates;
479 unsigned NunchukNearGUpdates;
480 // audio
481 HANDLE SampleThread;
482 const wiimote_sample* volatile CurrentSample; // otherwise playing square wave
483 // state recording
484 struct recording
485 {
486 volatile bool bEnabled;
487 state_history *StateHistory;
488 volatile DWORD StartTimeMS;
489 volatile DWORD EndTimeMS; // can be UNTIL_STOP
490 unsigned TriggerFlags; // wiimote changes trigger a state event
491 unsigned ExtTriggerFlags;// extension changes "
492 } Recording;
493 };
494
495 #endif // _WIIMOTE_H

tadas_AT_dailyda_DOT_com
ViewVC Help
Powered by ViewVC 1.1.8