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