From fcca48f210141e14e558a7064bcc6250e7aaf6fb Mon Sep 17 00:00:00 2001 From: Dennis Peeten Date: Sun, 25 May 2008 21:48:24 +0000 Subject: --- matchblox/glew32.dll | Bin 0 -> 212992 bytes matchblox/include/wiimote.h | 335 ++++++++++++++++++++++++++++++++ matchblox/include/wiimote_state.h | 391 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 726 insertions(+) create mode 100644 matchblox/glew32.dll create mode 100644 matchblox/include/wiimote.h create mode 100644 matchblox/include/wiimote_state.h diff --git a/matchblox/glew32.dll b/matchblox/glew32.dll new file mode 100644 index 0000000..6ae9984 Binary files /dev/null and b/matchblox/glew32.dll differ diff --git a/matchblox/include/wiimote.h b/matchblox/include/wiimote.h new file mode 100644 index 0000000..e208111 --- /dev/null +++ b/matchblox/include/wiimote.h @@ -0,0 +1,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 +#include // auto Unicode/Ansi support +#include // for HID write method +#include // for state recording + using namespace std; + + +#ifdef _MSC_VER // VC-specific: _DEBUG build only _ASSERT() sanity checks +# include +#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_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 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 \ No newline at end of file diff --git a/matchblox/include/wiimote_state.h b/matchblox/include/wiimote_state.h new file mode 100644 index 0000000..e7add28 --- /dev/null +++ b/matchblox/include/wiimote_state.h @@ -0,0 +1,391 @@ +// _______________________________________________________________________________ +// +// - 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_state.h (tab = 4 spaces) + +// the 'wiimote_state' struct contains all the Wiimote and Extension state data +// (buttons etc) - the wiimote class inherits from this and the app can poll +// the data there at any time. +#ifdef _MSC_VER // VC +# pragma once +#endif + +#ifndef _WIIMOTE_STATE_H +# define _WIIMOTE_STATE_H + +// speaker support: +enum speaker_freq + { + // (keep in sync with FreqLookup in wiimote.cpp) + FREQ_NONE = 0, + // my PC can't keep up with these using bUseHIDwrite, so I haven't + // been able to tune them yet + FREQ_4200HZ = 1, + FREQ_3920HZ = 2, + FREQ_3640HZ = 3, + FREQ_3360HZ = 4, + // these were tuned until the square-wave was glitch-free on my remote - + // may not be exactly right + FREQ_3130HZ = 5, // +190 + FREQ_2940HZ = 6, // +180 + FREQ_2760HZ = 7, // +150 + FREQ_2610HZ = 8, // +140 + FREQ_2470HZ = 9, + }; + +// wiimote_sample - holds the audio sample in the native wiimote format +struct wiimote_sample + { + wiimote_sample() : samples(NULL), length(0), freq(FREQ_NONE) {} + BYTE* samples; + DWORD length; + speaker_freq freq; + }; + +// flags & masks that indicate which part(s) of the wiimote state have changed +enum state_change_flags + { + // state didn't change at all + NO_CHANGE = 0, + + // Wiimote specific + CONNECTION_LOST = 1<<0, + BATTERY_CHANGED = 1<<1, + LEDS_CHANGED = 1<<2, // (probably redudant as wiimmote never + BUTTONS_CHANGED = 1<<3, // changes them unless requested) + ACCEL_CHANGED = 1<<4, + ORIENTATION_CHANGED = 1<<5, + IR_CHANGED = 1<<6, + // all wiimote flags + WIIMOTE_CHANGED = CONNECTION_LOST|BATTERY_CHANGED|LEDS_CHANGED| + BUTTONS_CHANGED|ACCEL_CHANGED|ORIENTATION_CHANGED| + IR_CHANGED, + // Extensions - general + EXTENSION_DISCONNECTED = 1<<7, + EXTENSION_PARTIALLY_INSERTED = 1<<8, + + // Nunchuk specific + NUNCHUK_CONNECTED = 1<<9, + NUNCHUK_BUTTONS_CHANGED = 1<<10, + NUNCHUK_ACCEL_CHANGED = 1<<11, + NUNCHUK_ORIENTATION_CHANGED = 1<<12, + NUNCHUK_JOYSTICK_CHANGED = 1<<13, + // all flags + NUNCHUK_CHANGED = NUNCHUK_CONNECTED|NUNCHUK_BUTTONS_CHANGED| + NUNCHUK_ACCEL_CHANGED|NUNCHUK_ORIENTATION_CHANGED| + NUNCHUK_JOYSTICK_CHANGED, + // Classic Controller + CLASSIC_CONNECTED = 1<<14, + CLASSIC_BUTTONS_CHANGED = 1<<15, + CLASSIC_JOYSTICK_L_CHANGED = 1<<16, + CLASSIC_JOYSTICK_R_CHANGED = 1<<17, + CLASSIC_TRIGGERS_CHANGED = 1<<18, + // all flags + CLASSIC_CHANGED = CLASSIC_CONNECTED|CLASSIC_BUTTONS_CHANGED| + CLASSIC_JOYSTICK_L_CHANGED| + CLASSIC_JOYSTICK_R_CHANGED|CLASSIC_TRIGGERS_CHANGED, + // ALL extension-related flags + EXTENSION_CHANGED = EXTENSION_DISCONNECTED| + NUNCHUK_CHANGED|CLASSIC_CHANGED, + // ALL flags + CHANGED_ALL = WIIMOTE_CHANGED|EXTENSION_CHANGED, + }; + +// wiimote_state (contains the Wiimote and Extension data and settings) +struct wiimote_state + { + friend class wiimote; // for Clear() + + // calibration information (stored on the Wiimote) + struct calibration_info + { + BYTE X0, Y0, Z0; + BYTE XG, YG, ZG; + } CalibrationInfo; + + // button state: + struct buttons + { + // convenience accessors + inline bool A () const { return (Bits & _A) != 0; } + inline bool B () const { return (Bits & _B) != 0; } + inline bool Plus () const { return (Bits & PLUS) != 0; } + inline bool Home () const { return (Bits & HOME) != 0; } + inline bool Minus () const { return (Bits & MINUS) != 0; } + inline bool One () const { return (Bits & ONE) != 0; } + inline bool Two () const { return (Bits & TWO) != 0; } + inline bool Up () const { return (Bits & UP) != 0; } + inline bool Down () const { return (Bits & DOWN) != 0; } + inline bool Left () const { return (Bits & LEFT) != 0; } + inline bool Right () const { return (Bits & RIGHT) != 0; } + + // all 11 buttons stored as bits (set = pressed) + WORD Bits; + + // button bit masks (little-endian order) + enum mask + { + LEFT = 0x0001, + RIGHT = 0x0002, + DOWN = 0x0004, + UP = 0x0008, + PLUS = 0x0010, + TWO = 0x0100, + ONE = 0x0200, + _B = 0x0400, // ie. trigger + _A = 0x0800, + MINUS = 0x1000, + HOME = 0x8000, + // + ALL = LEFT|RIGHT|DOWN|UP|PLUS|TWO|ONE|_A|_B|MINUS|HOME, + }; + } Button; + + // accelerometers state: + struct acceleration + { + BYTE RawX, RawY, RawZ; + float X, Y, Z; + + // note: experimental! the orientation values can only be safely estimated + // if the controller isn't accelerating (otherwise there is no + // simple way to seperate orientation from acceleration - except + // perhaps using the IR reference and/or some clever assumptions). + // so for now the code only updates orientation if the controller + // appear to be stationary (by checking if the acceleration vector + // length is near 1G for several updates in a row). + // also note that there is no way to detect Yaw from the accelerometer + // alone when it's pointing at the screen (and I'm not curently + // processing IR): + struct orientation + { + float X, Y, Z; + unsigned UpdateAge; // how many acceleration updates ago the last + // orientation estimate was made (if this + // value is high, the values are out-of-date + // and probably shouldn't be used). + // Euler angle support (useful for some things). + // * note that decomposing to Euler angles is complex, not always reliable, + // and also depends on your assumptions about the order each component + // is applied in. you may need to handle this yourself for more + // complex scenarios * + float Pitch; // in degrees (-180 - +180) + float Roll; // " + // float Yaw; + } Orientation; + } Acceleration; + + // IR camera state: + struct ir + { + // in theory the IR imager is 1024x768 and so should report raw coords + // 0-1023 x 0-767. in practice I have never seen them exceed the values + // below, so I'm using them instead to give the full 0-1 float range + // (it's possible that the edge pixels are used for processing, or masked + // out due to being unreliable) + static const unsigned MAX_RAW_X = 1016; + static const unsigned MAX_RAW_Y = 760; + + // data mode reported by the IR sensor + enum mode + { + OFF = 0x00, + BASIC = 0x01, // 10 bytes + EXTENDED = 0x03, // 12 bytes + FULL = 0x05, // 16 bytes * 2 (format unknown) + }; + mode Mode; + + struct dot + { + bool bFound; // wiimote can see it + unsigned RawX; + unsigned RawY; + float X; // 0-1 (left-right) + float Y; // " (top -bottom) + int Size; // (not available in BASIC mode) + } Dot[4]; + } IR; + + struct leds + { + // all LEDs stored in bits 0-3 (1 = lit) + BYTE Bits; + + // convenience accessors: + inline bool Lit (unsigned index) + { _ASSERT(index < 4); + return (index >= 4)? false : ((Bits & (1<