summaryrefslogtreecommitdiffstats
path: root/wiimote_ir_smoothing/wiimote.h
blob: e20811185671b95a335f7c1e8911e292436eba8b (plain)
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
// _______________________________________________________________________________
//
//	 - WiiYourself! - native C++ Wiimote library  v1.00
//	  (c) gl.tter 2007-8 - http://gl.tter.org
//
//	  see License.txt for conditions of use.  see History.txt for change log.
// _______________________________________________________________________________
//
//  wiimote.h  (tab = 4 spaces)

#ifdef _MSC_VER // VC
# pragma once
#endif

#ifndef _WIIMOTE_H
# define _WIIMOTE_H

#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
 using namespace std;

 
#ifdef _MSC_VER  // VC-specific: _DEBUG build only _ASSERT() sanity checks
# include <crtdbg.h>
#else
# define _ASSERT // (add your compiler's implementation if you like)
#endif

#include "wiimote_state.h"

// configs:
 //  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
//  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	0
#define	WIIYOURSELF_VERSION_MINOR2	0
#define WIIYOURSELF_VERSION_STR		_T("1.00")

// 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.  'changed' is a combination
//  of flags indicating which state has changed since the last callback.
typedef void (*state_changed_callback)	(class wiimote	    &owner,
										 state_change_flags changed);

// internals
typedef BOOLEAN (__stdcall *hidwrite_ptr)(HANDLE HidDeviceObject,
										  PVOID  ReportBuffer,
										  ULONG  ReportBufferLength);

// 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 ();
		~wiimote ();

	public:
		// 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)
			};


	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	NunchukConnected () const { return (Internal.bExtension && (Internal.ExtensionType==wiimote_state::NUNCHUK)); }
		inline bool	ClassicConnected ()	const { return (Internal.bExtension && (Internal.ExtensionType==wiimote_state::CLASSIC)); }
		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
		// 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;

		// get the button name from its bit index (some bits are unused)
		static const TCHAR*		 ButtonNameFromBit		  [16];
		// same for the Classic Controller
		static const TCHAR*		 ClassicButtonNameFromBit [16];
		// get the frequency from speaker_freq enum
		static const unsigned	 FreqLookup				  [10];

	public: // methods
		// call Connect() first - returns true if wiimote was found & enabled
		//  - 'wiimote_index' specifies which *installed* (not necessarily
		//     *connected*) wiimote should be tried (0 = first).
		//    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);
		
		// 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.
		inline state_change_flags RefreshState ()	{ return _RefreshState(false); }

		// 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.
		struct state_event {
			DWORD		  time_ms;	// system timestamp in milliseconds
			wiimote_state state;
			};
		typedef 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 specify specific state changes that will trigger an insert
		//    into the history (others are 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);
		// initializes an extension when plugged in.
		void InitializeExtension	();
		// decrypts data sent from the extension
		void DecryptBuffer			(BYTE* buff, unsigned size);
		// 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		();
		// 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; }
		// copy the accumulated parsed state into the public state
		state_change_flags _RefreshState (bool for_callbacks);

		// 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					= 0x04b00030;
		static const int REGISTER_IR_SENSITIVITY_1		= 0x04b00000;
		static const int REGISTER_IR_SENSITIVITY_2		= 0x04b0001a;
		static const int REGISTER_IR_MODE				= 0x04b00033;
		static const int REGISTER_EXTENSION_INIT		= 0x04a40040;
		static const int REGISTER_EXTENSION_TYPE		= 0x04a400fe;
		static const int REGISTER_EXTENSION_CALIBRATION	= 0x04a40020;

		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	 bConnectionLost; // auto-Disconnect()s if set
		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 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;
		// async Hidd_WriteReport() thread
		HANDLE			 HIDwriteThread;
		queue<BYTE*>	 HIDwriteQueue;
		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;
	};

#endif // _WIIMOTE_H