From ef1172e936c054946cb8a4b5bed7e995b3136ebb Mon Sep 17 00:00:00 2001 From: Wei Shuai Date: Fri, 26 Jan 2018 13:51:39 +0800 Subject: opentrack/wiiyourself: new home --- tracker-pt/wiiyourself/CMakeLists.txt | 1 - tracker-pt/wiiyourself/History.txt | 281 ---- tracker-pt/wiiyourself/License.txt | 42 - tracker-pt/wiiyourself/ReadMe.txt | 202 --- tracker-pt/wiiyourself/lang/nl_NL.ts | 4 - tracker-pt/wiiyourself/lang/ru_RU.ts | 4 - tracker-pt/wiiyourself/lang/stub.ts | 4 - tracker-pt/wiiyourself/wiimote.cpp | 2806 ------------------------------- tracker-pt/wiiyourself/wiimote.h | 495 ------ tracker-pt/wiiyourself/wiimote_common.h | 109 -- tracker-pt/wiiyourself/wiimote_state.h | 379 ----- 11 files changed, 4327 deletions(-) delete mode 100644 tracker-pt/wiiyourself/CMakeLists.txt delete mode 100644 tracker-pt/wiiyourself/History.txt delete mode 100644 tracker-pt/wiiyourself/License.txt delete mode 100644 tracker-pt/wiiyourself/ReadMe.txt delete mode 100644 tracker-pt/wiiyourself/lang/nl_NL.ts delete mode 100644 tracker-pt/wiiyourself/lang/ru_RU.ts delete mode 100644 tracker-pt/wiiyourself/lang/stub.ts delete mode 100644 tracker-pt/wiiyourself/wiimote.cpp delete mode 100644 tracker-pt/wiiyourself/wiimote.h delete mode 100644 tracker-pt/wiiyourself/wiimote_common.h delete mode 100644 tracker-pt/wiiyourself/wiimote_state.h (limited to 'tracker-pt/wiiyourself') diff --git a/tracker-pt/wiiyourself/CMakeLists.txt b/tracker-pt/wiiyourself/CMakeLists.txt deleted file mode 100644 index e16787c7..00000000 --- a/tracker-pt/wiiyourself/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -otr_module(tracker-wii-pt-wiiyourself STATIC) diff --git a/tracker-pt/wiiyourself/History.txt b/tracker-pt/wiiyourself/History.txt deleted file mode 100644 index 6601dc97..00000000 --- a/tracker-pt/wiiyourself/History.txt +++ /dev/null @@ -1,281 +0,0 @@ -____________________________________________________________ - - - WiiYourself! - native C++ Wiimote library v1.15 - (c) gl.tter 2007-10 - http://gl.tter.org -____________________________________________________________ - -History: - -What's new since 1.01a? - Main Features (see ReadMe & full history for details) - - + Balance Board support with automatic offset removal. - - + Seemingly solid MotionPlus support. - - + Library no longer includes project files - just add wiimote.cpp & - header to your project (avoids all build-settings releated issues) - - + better MinGW support: (thanks Elmo) - adds functional _ASSERT/TRACE/WARN/DEEP_TRACE macros - non-MSYS dependent build option via 'make_mingw.bat'. - Demo builds & works under MinGW. - - + new Python wrapper (by Robert Xiao, see 'Python' folder for details) - - + library now compiles on Borland (thanks Griff - demo not tested). - - + many fixes, connections should be more reliable. - - + join my mailing list to give feedback, share ideas & stay informed: - http://gl.tter.org/mailman/listinfo/wiiyourself_gl.tter.org - -1.15 Final: - + fixed MotionPlus detection on stacks that require HID writes! - + Balance Board corner weight values have been quartered (.Total remains - unchanged). The non-raw corner weight incorrectly reported 4x their - real value. - + Wiimote calibration info is more reliably received (it may not have - arrived in many instances) - + exposed a partially-unique device ID (wiimote::UniqueID). This 64bit - number is set during the Connect() call, and is derived from the - device-specific calibration values. It's therefore not guaranteed to - be truly unique (several devices may conceivably hold the same calibration - values). However in practice it is likely to be unique between a - few wiimotes, so it can be used to eg. assign a particular wiimote to the - same player every time. - + internal: made the HID report output queue fixed-size to remove any - glitches from frequent dynamic memory allocation (thanks - Steve). The old STL-queue based code can still be reenabled - by defining USE_DYNAMIC_HIDQUEUE. - -1.15 RC2: - + added Python wrapper by Robert Xiao - you can now use WiiYourself! with - Python! (thanks Robert) - + hopefully fixed MotionPlus connection problems! (send report to my - mailing list) - - + added virtual event-change notifier to the wiimote object (thanks Robert - Xio) - works the same as external callbacks. To use, derive your own - class from the wiimote object and override ChangedNotifier() - - + changed the way callbacks work: - - in previous versions, it was OK to access the wiimote object's state - from callbacks. This required an internal RefreshState() call just - before the callback function is executed - but this could then change - the internal state unexpectedly, so values could change in polling - loops even between the app's own RefreshState() calls. - - to correct this, the callback functions now get a read-only copy - of the newest state passed in, you should only access this copy as - state in the wiimote object is likely out-of-date. - - In short, the wiimote object's exposed state is now _only_ refreshed - by the application, not by callbacks. - - (this also solved Motion+ connection and disabling failures). - - + added new change event 'CONNECTED' - - the demo previously used callbacks to set most of its report types, but - it also set them once shortly after connecting the wiimote, and this could - cause it to set the wrong one, breaking extension data. Instead it now - uses the CONNECTED event in the callback. It's best to only set these - in one place. - -1.15 RC: - + fixed missing Balance Board calibration values for the 34kg category - (thanks Benjamin Lassort). - - + fixed Wiimote disconnecting in certain scenarios (ReportType wasn't - initialised, and this could sometimes be sent to the 'mote, causing - it to disconnect - thanks Robert Xiao). - - + minor changes to support Robert Xiao's WiiYourself! Python Wrapper! - (next release). - -1.14 BETA: - - added new state & callback event: bBatteryDrained / BATTERY_DRAINED - this is sent went the wiimote signals that the batteries are nearly - empty. - - added MotionPlus extension events (ie. for extensions plugged into it): - MOTIONPLUS_EXTENSION_CONNECTED - MOTIONPLUS_EXTENSION_DISCONNECTED - wiimote::MotionPlusHasExtension() - wiimote::DisableMotionPlus() - wiimote::EnableMotionPlus() - - (apps can now decide if they want to disable the MotionPlus to read the - extension instead, see demo for an example) - - ** however **, MotionPlus disabling isn't reliable at the moment (it - rarely works), and so extension connected to an already enabled plus - rarely are activated. Could use some help on this one. - -1.13 BETA: - + ** BALANCE BOARDS no longer require setting a report type! ** - there is only one type for it, and this is now set automatically. - - + 'At Rest' offset removal added (currently only for Balance Boards). - this reads the current analogue sensor values after a Connect() call, - and then subtracts them from future values, to remove any unwanted - offsets (currently ~ +- 0-2.5kg with Balance Boards). 'raw' values - are not affected. - - If the device was not at rest during Connect(), then the app can - remove the current offsets manually via CalibrateAtRest(). - - + ** PRELIMINARY MOTION PLUS SUPPORT! ** - - Motion Plus does not report itself until queried, so it's currently - queried every second. If detected, it is activated and is reported - like any other extension. Note that extensions plugged into the - MotionPlus itself can't currently be used at the same time (it's not - known if this is even possible). Right now you need to unplug the - MotionPlus to use another extension (I will add some way to toggle - the MotionPlus so that another extensions becomes available again) - in the next release. - - According to this interview with the MotionPlus designers - - there are two gyro sensitivity modes, but this has not been reverse - engineered yet. Also I'm not 100% certain of the correctness of the - values (although they seem right), or their actual scale (ie. how many - degrees rotations per second do the float values actually represent)? - - + the Demo has been updated for both devices. - + ReadMe has been updated with new relevant info. - -1.12 BETA: - + ** REMOVED ALL LIBRARY PROJECTS ** - instead just add wiimote.cpp to your application and include the header - as before (this removes all build/project related-problems, like matching - the runtime/Unicode settings etc). - - + Balance Board is now working (thanks to Akihiko's donation of a board!) - + added wiimote::IsBalanceBoard() (Balance Boards are detected as wiimotes - with a permanent BALANCE_BOARD extension). - NOTE: Balance Boards require the IN_BUTTONS_BALANCE_BOARD report type - (see demo). - + changed some of the wiimote_state extension enums to ID a wider variety. - + no more invalid acceleration values from devices that don't support it. - + fixed some project settings. - -1.11 BETA: - + new way to detect extensions (supposedly works on all of them, including - wireless Nunchuks) - only tested on stock Nunchuk. - + longer sleep after SetReportType (may help data not being reported). - -1.1 BETA: - + beta Balance Board support! - + better MinGW support: (thanks Elmo) - adds functional _ASSERT/TRACE/WARN/DEEP_TRACE macros - non-MSYS dependent build option via 'make_mingw.bat'. - Demo builds & works under MinGW. - + directory reorganisation: - - Each compiler has own project dir (VC2005/VC2005/MinGW), - and equivialent lib/ sudir. - + now ships with working VC2005 SP1 / VC2008 / MinGW libraries - (and MinGW DLL). - + library now compiles on Borland (thanks Griff) - demo may not. - -1.01a: (1.01 had incorrect version defines) - + extensions now work when already connected before Connect(), - & also when an EXT SetReportType() is used initially. - + ** renamed wiimote_state::IR::dot::bVisible to 'bVisible'. ** - + Disconnect() now waits for its threads to exit. - + made TRACE/WARN macros VC2005+ specific (as earlier VC versions don't - support variable arg macros). - + corrected wiimote.h Connect() comments (wiimote selection is 1-based, - not 0-based) - -1.00: - + ** major bug fix, write buffer was abused. ** might have caused various - problems. - + ** added delay to EnableIR(), fixed IR init problems for those that - had them (thanks Cameron) **. if you had to use your own delays - to get things to work, try removing them now. - + wiimote_state::classic_controller::buttons::TriggerL() / R were reversed - (thanks Vico). - + patch & Makefile for MSYS / MinGW (thanks Dario). - + updated ReadMe. - -0.99b: - + added support for the Guitar Hero controller (thanks Morgan). - It's just a Classic Controller with a different ID and is read the same, - but can be differentiated via wiimote_state::extension_type::CLASSIC_GUITAR. - -0.96b: - + fix ClassicButtonNameFromBit[] - + fix WIIYOURSELF_VERSION_MINOR2 - -0.95b: - + Classic Controller button fixes (thanks Farshid). - + sightly longer Sleep() in Reset() - hopefully fixes some reports of - wiimote acceleration values not working. - -0.94b: - + deadzones weren't working. - -0.93b: - + ** compiled libs are now stored in /libs ** - + ** up to 4 dots are now available in every IR mode ** - + some 'state_change_flags' weren't quite generated correctly. - - removed 'wiimote_state::polling' flags (redundant, flags are already - returned via RefreshState()). - + various internal improvements - -0.92b: - ** Polling changes ** - - now need to call RefreshState() once before each polling pass (see - header comments & demo). this was done to synchronise the threaded - state updates, so that data integrity is guaranteed. - ** Callback changes ** - - combined 'wiimote_state_changed' and 'extension_state_changed' flags - into 'state_change_flags - - removed 'ExtensionChangedCallback' (only a single callback is used now) - - added 'CallbackTriggerFlags' to minimize callback overhead - (see header comments & demo) - + added Reset() (see header comments) - + button mask TRIGGER is now _B - - Demo: removed 'wiimote2' line (debug leftover) - -0.82b: - ** code/demo failed pre-XP (HID writes require XP+). code now detects - HID write support dynamically. ** - + tidied code & surpressed redundant warning (or just enable C++ exceptions). - + Improved debug output (mainly DEEP_TRACE) - + Connect() can now take (and defaults to) 'FIRST_AVAILABLE' as the wiimote - index (see header comments). - + 'wiimote_sample' is now auto-cleared on construction - + Adjusted max 'theoretical' raw IR coord values (1023x767) to largest - actually observed, to output full 0-1 float range. - + **Inverted** IR X float coord to match traditional 'left = 0' convention - (raw coords unaffected for now). - + Added state recording ability to aid state/motion analysis. See RecordState(); - - removed RequestBatteryUpdate() (battery level is now periodically refreshed) - - disabled ...CALIBRATION_CHANGED flags (not useful) - - Demo : should now work pre-XP. - ReadMe: added Wiimote/PC installation notes (MS stack is especially tricky). - -0.81b: - + connection loss is now detected (via failed writes) - + ConnectionWasLost() added - + report modes renamed for clarity. - + Connect(): added 'force_hidwrites' (for testing only). - + Extension connections now seem to be reliable. - + Battery is now periodically refreshed (also used for loss detection) - + 'BatteryRaw' was set incorrectly - + added 'wiimote::ClassicButtonNameFromBit[]' - - + Demo : Classic Controller data shown. - + Demo : IR dot sizes now reported when possible (only if extension - data isn't requested as they're not available then). - - + License: 'no harm' clause added. - + ReadMe : added build notes etc. - -0.1b: - First release. \ No newline at end of file diff --git a/tracker-pt/wiiyourself/License.txt b/tracker-pt/wiiyourself/License.txt deleted file mode 100644 index fc41a9a0..00000000 --- a/tracker-pt/wiiyourself/License.txt +++ /dev/null @@ -1,42 +0,0 @@ -____________________________________________________________ - - - WiiYourself! - native C++ Wiimote library v1.15 - (c) gl.tter 2007-10 - http://gl.tter.org -____________________________________________________________ - - LICENSE: My Wiimote library is free for any use (including - commercial), with the following conditions: - -1) You may not use it to harm anyone, directly or - indirectly. * this includes, but is not limited to, any - kind of direct or indirect MILITARY use or related - research * - - (but bruising egos is fine ;). - -2) Any distribution in binary form (ie. linked with your - program) must include the following text in your - distribiutions's documentation (ReadMe file, help file, - About box and/or splash screen): - - "contains WiiYourself! wiimote code by gl.tter - http://gl.tter.org" - -3) Any distribution in source code form must keep all my - copyright notices intact unmodified (you can add to - them if you've made changes), and must include this - license text (either include this file in your - distribution, or paste its contents into your - distribution's own licence file). - -4) You may not use the code to produce a competing - library, unless you rewrite all of it considerably - (for example to convert it to another language, but - you need to contact me for written permission first). - - Instead please contribute new features, fixes and ideas - to my mailining list (see ReadMe.txt). -__ - -gl.tter (http://gl.tter.org | glATr-i-lDOTnet) - diff --git a/tracker-pt/wiiyourself/ReadMe.txt b/tracker-pt/wiiyourself/ReadMe.txt deleted file mode 100644 index 85ca0034..00000000 --- a/tracker-pt/wiiyourself/ReadMe.txt +++ /dev/null @@ -1,202 +0,0 @@ -__________________________________________________________________ - - - WiiYourself! - native C++ Wiimote library v1.15 - (c) gl.tter 2007-10 - http://gl.tter.org -__________________________________________________________________ - -This marks the likely-final release of my free & fully-featured - Wiimote native C++ library for Windows. - -Originally based on Brian Peek's 'Managed Wiimote Library' - (http://blogs.msdn.com/coding4fun/archive/2007/03/14/1879033.aspx) - I then rewrote and extended it considerably. - -There's no documentation - check Brian's article for a good -overview and general 'Wiimote with Windows' info - but the -source code has extensive comments, and the demo app should help -you make sense of it all. Any questions, join my mailing list -(below). - -Check License.txt for the (few) conditions of use, and - History.txt for important changes from previous versions. -_____ - -Notes: - - - the library consists only of wiimote.cpp & wiimote.h. Simply add - them to your project and include the header. - - - VC 2005 & 2008 projects, and a MSYS makefile for MinGW for the - demo program are included. - - - for MSYS: - at the MSYS prompt type: make -f Makefile.MSYS - (it will create a folder named MinGW whith the binaries - and proper folder structure) - - - The Windows Driver Development Kit (WDK or DDK) is required to - build (for the HID API). It's a free download from MS page - (no need to register). Currently it's found here (or search - for it on microsoft.com): - - 'Windows Driver Kit (WDK) 7.1.0' - http://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx - - Unfortunately it changes frequently so the instructions below - may be wrong or incomplete. Google or ask on my mailing - list for help if needed: - - - add its 'inc' and 'inc/api' dirs to your include paths - (make sure they are *above* other paths, ie. searched first) - - - add its 'lib/win7/i386' dir to your library paths. - - You can also use earlier versions known as the 'DDK'. The parts - of the HID API I use haven't changed in a long time. These paths - are usually used: - - - add 'inc/wxp' dir to your include paths (*above* others) - - add 'lib/wxp/i386' to your library paths. - - Notes: - - do _not_ add any 'STL' paths from the WDK/DDK as they can cause - build problems. - - the order of your include paths can be important. If you placed - them above the paths and are still getting compilation errors, - try moving them around. - - - The library is Unicode-ready via (see demo project). - - - if you're not using VC you need to link with these libraries: - setupapi.lib - winmm.lib - hid.lib (from the DDK) -__________________________ - -Wiimote installation notes: - - The Wiimote needs to be 'paired' (Bluetooth connected) with the - PC before you can install/use it. Pressing 1 & 2 simultaneously - puts it into 'discoverable' mode for a few seconds (LEDs will - flash - the number of LEDs reflects the battery level). - - It will be detected as 'Nintendo RVL-CNT-01'. - - Stack-specific instructions: - - - Windows' built-in Bluetooth stack: - - 1) open up the Bluetooth control panel. - 2) press _and hold_ 1 & 2 on the wiimote (LEDs flash) until the - installation is complete (otherwise the wiimote usually times - out half-way through the procedure, and although it may seem - to have installed it's never 'connected' and doesn't work). - 3) add a new device - it should find it. don't use a password. - 4) when the installation is fully complete, let go of 1&2. The - Bluetooth panel should now show it 'connected'. - - if something goes wrong you need to uninstall it and try again. - - if you un-pair the wiimote later (see below), it seems you need - to remove and install it all over again to get it to work (if you - know a workaround, let me know). - - - Toshiba stack: - - straight forward, press 1 & 2 on the Wiimote (you don't need to - hold them if you're quick) and click 'New Connection'. - - once found, you can pair it anytime again by right-clicking its - device icon (and pressing 1 & 2 as before) - you can also set up - a desktop shortcut that enters discovery mode immediately. - - - Widcomm stack: - - 1) Open 'My Bluetooth Places'. - 2) Press and hold 1 & 2 (until the process is complete). - 3) Click 'View Devices in Range'. - 4) Wiimote is detected as Nintendo RVL-CNT-01. - 5) Select it, then click 'Bluetooth Setup Wizard'. - 5) Click 'Skip' (no password). - 6) Now it should be connected (you can let go of 1 & 2). - - Troubleshooting: - - the device seems to be connected but the Demo can't find it - (CreateFile() fails with error 5 'Access Denied'), - or - it disconnects almost immediately after connection - or - asks for a password a few seconds after connection - - Try uninstalling all HID devices from Device Manager, and then - redetecting them with 'Scan for hardware changes' (I had all - these problems and that fixed it for me). - - - Other stacks - - similar to the above (contribute instructions?) - - - - Disconnect/un-pair to save power (any stack): - - hold the Wiimote Power button for a few seconds - it automatically - unpairs itself, re-enters pairing mode for a few seconds - (flashing LEDs), then times out and (effecively) switches off. - -__________________________ - -Balance Boards notes: - - Balance Boards are installed using the same procedure as wiimotes, by - holding the 'Sync' button in the battery compartment. The Bluetooth - stack detectes them as 'Nintendo RVL-WBC-01'. - - They report to the library as wiimotes, with a permanent BALANCE_BOARD - extension. They only have one button (A), and no IR/Acceleration/Rumble - or Speaker support. There is only one supported 'report type' so the - library sets this automatically (see the demo for details). - - You can detect them with the wiimote::IsBalanceBoard() call. - - The boards tested so far all report up to ~ +-2.5KB weight offsets even - where there is no weight placed on them (ie. 'at rest'). It is unknown - if this is normal sensor inaccuracy/drift, or if the calibration values - read from the board are interpreted incorrectly. For now the library - automatically subtracts the first incoming sensor values after a Connect() - call from all future non-raw values (the raw values are never modified). - - The offsets used are exposed in wiimote_state::balance_board::AtRestKG. - - - if the board wasn't at rest during the Connect() call these offsets - will be wrong - the app can measure them manually (when the board - is at rest) by calling wiimote::CalibrateAtRest(). - -__________________________ - -MotionPlus notes: - - Once activated it's reported like any other extension, but needs to be - manually enabled (see demo for an example): - - Test first if it's connected via MotionPlusConnected() - - Then call EnableMotionPlus() - - It will then replace any other extension connected to it, unil you - disable the Motion+ again with DisableMotionPlus(). - - The speed values are believed to be correct. The calibration values are not - yet understood, so the data is currently uncalibrated. - - Some technical details are mentioned in this interview with the MotionPlus - hardware/software engineers: - http://uk.wii.com/wii/en_GB/software/iwata_asks_motionplus_volume_1_2162.html#top - - - * Special thanks to the guys at WiiBrew.org, and all contributing hackers * - for figuring out & documenting the wiimote protocols: - http://wiibrew.org/wiki/Wiimote/Extension_Controllers#Wii_Motion_Plus - - -Sign up to the mailing list to stay in the loop, exchange ideas or help each - other out: http://gl.tter.org/mailman/listinfo/wiiyourself_gl.tter.org -__ - -gl.tter (glATr-i-lDOTnet) - - diff --git a/tracker-pt/wiiyourself/lang/nl_NL.ts b/tracker-pt/wiiyourself/lang/nl_NL.ts deleted file mode 100644 index 9e739505..00000000 --- a/tracker-pt/wiiyourself/lang/nl_NL.ts +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tracker-pt/wiiyourself/lang/ru_RU.ts b/tracker-pt/wiiyourself/lang/ru_RU.ts deleted file mode 100644 index f62cf2e1..00000000 --- a/tracker-pt/wiiyourself/lang/ru_RU.ts +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tracker-pt/wiiyourself/lang/stub.ts b/tracker-pt/wiiyourself/lang/stub.ts deleted file mode 100644 index 6401616d..00000000 --- a/tracker-pt/wiiyourself/lang/stub.ts +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tracker-pt/wiiyourself/wiimote.cpp b/tracker-pt/wiiyourself/wiimote.cpp deleted file mode 100644 index 46130cca..00000000 --- a/tracker-pt/wiiyourself/wiimote.cpp +++ /dev/null @@ -1,2806 +0,0 @@ -// _______________________________________________________________________________ -// -// - 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.cpp (tab = 4 spaces) - -// VC-specifics: -#ifdef _MSC_VER - // disable warning "C++ exception handler used, but unwind semantics are not enabled." - // in (I don't use it - or just enable C++ exceptions) -# pragma warning(disable: 4530) -// auto-link with the necessary libs -# pragma comment(lib, "setupapi.lib") -# pragma comment(lib, "hid.lib") // for HID API (from DDK) -# pragma comment(lib, "winmm.lib") // for timeGetTime() -#endif // _MSC_VER - -#include "wiimote.h" -#include -extern "C" { -# ifdef __MINGW32__ -# include // from WinDDK -# else -# include -# endif -} -#include // for _stat -#include // " -#include // for _beginthreadex() -#ifdef __BORLANDC__ -# include // for orientation -#else -# include // " -#endif -#include // for WAVEFORMATEXTENSIBLE -#include // for timeGetTime() - -// apparently not defined in some compilers: -#ifndef min -# define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif -// ------------------------------------------------------------------------------------ -// helpers -// ------------------------------------------------------------------------------------ -template inline T sign (const T& val) { return (val<0)? T(-1) : T(1); } -template inline T square(const T& val) { return val*val; } -#define ARRAY_ENTRIES(array) (sizeof(array)/sizeof(array[0])) - -// ------------------------------------------------------------------------------------ -// Tracing & Debugging -// ------------------------------------------------------------------------------------ -#define PREFIX _T("WiiYourself! : ") - -// comment these to auto-strip their code from the library: -// (they currently use OutputDebugString() via _TRACE() - change to suit) -#if (_MSC_VER >= 1400) // VC 2005+ (earlier versions don't support variable args) -# define TRACE(fmt, ...) _TRACE(PREFIX fmt _T("\n"), __VA_ARGS__) -# define WARN(fmt, ...) _TRACE(PREFIX _T("* ") fmt _T(" *") _T("\n"), __VA_ARGS__) -#elif defined(__MINGW32__) -# define TRACE(fmt, ...) _TRACE(PREFIX fmt _T("\n") , ##__VA_ARGS__) -# define WARN(fmt, ...) _TRACE(PREFIX _T("* ") fmt _T(" *") _T("\n") , ##__VA_ARGS__) -#endif -// uncomment any of these for deeper debugging: -//#define DEEP_TRACE(fmt, ...) _TRACE(PREFIX _T("|") fmt _T("\n"), __VA_ARGS__) // VC 2005+ -//#define DEEP_TRACE(fmt, ...) _TRACE(PREFIX _T("|") fmt _T("\n") , ##__VA_ARGS__) // mingw -//#define BEEP_DEBUG_READS -//#define BEEP_DEBUG_WRITES -//#define BEEP_ON_ORIENTATION_ESTIMATE -//#define BEEP_ON_PERIODIC_STATUSREFRESH - -// internals: auto-strip code from the macros if they weren't defined -#ifndef TRACE -# define TRACE -#endif -#ifndef DEEP_TRACE -# define DEEP_TRACE -#endif -#ifndef WARN -# define WARN -#endif -// ------------------------------------------------------------------------------------ -static void _cdecl _TRACE (const TCHAR* fmt, ...) - { - static TCHAR buffer[256]; - if (!fmt) return; - - va_list argptr; - va_start (argptr, fmt); -#if (_MSC_VER >= 1400) // VC 2005+ - _vsntprintf_s(buffer, ARRAY_ENTRIES(buffer), _TRUNCATE, fmt, argptr); -#else - _vsntprintf (buffer, ARRAY_ENTRIES(buffer), fmt, argptr); -#endif - va_end (argptr); - - OutputDebugString(buffer); - } - -// ------------------------------------------------------------------------------------ -// wiimote -// ------------------------------------------------------------------------------------ -// class statics -HMODULE wiimote::HidDLL = NULL; -unsigned wiimote::_TotalCreated = 0; -unsigned wiimote::_TotalConnected = 0; -hidwrite_ptr wiimote::_HidD_SetOutputReport = NULL; - -// (keep in sync with 'speaker_freq'): -const unsigned wiimote::FreqLookup [TOTAL_FREQUENCIES] = - { 0, 4200, 3920, 3640, 3360, - 3130, 2940, 2760, 2610, 2470 }; - -const TCHAR* wiimote::ButtonNameFromBit [TOTAL_BUTTON_BITS] = - { _T("Left") , _T("Right"), _T("Down"), _T("Up"), - _T("Plus") , _T("??") , _T("??") , _T("??") , - _T("Two") , _T("One") , _T("B") , _T("A") , - _T("Minus"), _T("??") , _T("??") , _T("Home") }; - -const TCHAR* wiimote::ClassicButtonNameFromBit [TOTAL_BUTTON_BITS] = - { _T("??") , _T("TrigR") , _T("Plus") , _T("Home"), - _T("Minus"), _T("TrigL") , _T("Down") , _T("Right") , - _T("Up") , _T("Left") , _T("ZR") , _T("X") , - _T("A") , _T("Y") , _T("B") , _T("ZL") }; -// ------------------------------------------------------------------------------------ -wiimote::wiimote () - : - DataRead (CreateEvent(NULL, FALSE, FALSE, NULL)), - Handle (INVALID_HANDLE_VALUE), - ReportType (IN_BUTTONS), - bStatusReceived (false), // for output method detection - bConnectInProgress (true ), - bInitInProgress (false), - bEnablingMotionPlus (false), - bConnectionLost (false), // set if write fails after connection - bMotionPlusDetected (false), - bMotionPlusEnabled (false), - bMotionPlusExtension (false), - bCalibrateAtRest (false), - bUseHIDwrite (false), // if OS supports it - ChangedCallback (NULL), - CallbackTriggerFlags (CHANGED_ALL), - InternalChanged (NO_CHANGE), - CurrentSample (NULL), - HIDwriteThread (NULL), - ReadParseThread (NULL), - SampleThread (NULL), - AsyncRumbleThread (NULL), - AsyncRumbleTimeout (0), - UniqueID (0) // not _guaranteed_ unique, see comments in header -#ifdef ID2_FROM_DEVICEPATH // (see comments in header) - // UniqueID2 (0) -#endif - { - _ASSERT(DataRead != INVALID_HANDLE_VALUE); - - // if this is the first wiimote object, detect & enable HID write support - if(++_TotalCreated == 1) - { - HidDLL = LoadLibrary(_T("hid.dll")); - _ASSERT(HidDLL); - if(!HidDLL) - WARN(_T("Couldn't load hid.dll - shouldn't happen!")); - else{ - _HidD_SetOutputReport = (hidwrite_ptr) - GetProcAddress(HidDLL, "HidD_SetOutputReport"); - if(_HidD_SetOutputReport) - TRACE(_T("OS supports HID writes.")); - else - TRACE(_T("OS doesn't support HID writes.")); - } - } - - // clear our public and private state data completely (including deadzones) - Clear (true); - Internal.Clear(true); - - // and the state recording vars - memset(&Recording, 0, sizeof(Recording)); - - // for overlapped IO (Read/WriteFile) - memset(&Overlapped, 0, sizeof(Overlapped)); - Overlapped.hEvent = DataRead; - Overlapped.Offset = - Overlapped.OffsetHigh = 0; - - // for async HID output method - InitializeCriticalSection(&HIDwriteQueueLock); - // for polling - InitializeCriticalSection(&StateLock); - - // request millisecond timer accuracy - timeBeginPeriod(1); - } -// ------------------------------------------------------------------------------------ -wiimote::~wiimote () - { - Disconnect(); - - // events & critical sections are kept open for the lifetime of the object, - // so tidy them up here: - if(DataRead != INVALID_HANDLE_VALUE) - CloseHandle(DataRead); - - DeleteCriticalSection(&HIDwriteQueueLock); - DeleteCriticalSection(&StateLock); - - // tidy up timer accuracy request - timeEndPeriod(1); - - // release HID DLL (for dynamic HID write method) - if((--_TotalCreated == 0) && HidDLL) - { - FreeLibrary(HidDLL); - HidDLL = NULL; - _HidD_SetOutputReport = NULL; - } - } - -// ------------------------------------------------------------------------------------ -bool wiimote::Connect (unsigned wiimote_index, bool force_hidwrites) - { - if(wiimote_index == FIRST_AVAILABLE) - TRACE(_T("Connecting first available Wiimote:")); - else - TRACE(_T("Connecting Wiimote %u:"), wiimote_index); - - // auto-disconnect if user is being naughty - if(IsConnected()) - Disconnect(); - - // get the GUID of the HID class - GUID guid; - HidD_GetHidGuid(&guid); - - // get a handle to all devices that are part of the HID class - // Brian: Fun fact: DIGCF_PRESENT worked on my machine just fine. I reinstalled - // Vista, and now it no longer finds the Wiimote with that parameter enabled... - HDEVINFO dev_info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE);// | DIGCF_PRESENT); - if(!dev_info) { - WARN(_T("couldn't get device info")); - return false; - } - - // enumerate the devices - SP_DEVICE_INTERFACE_DATA didata; - didata.cbSize = sizeof(didata); - - unsigned index = 0; - unsigned wiimotes_found = 0; - while(SetupDiEnumDeviceInterfaces(dev_info, NULL, &guid, index, &didata)) - { - // get the buffer size for this device detail instance - DWORD req_size = 0; - SetupDiGetDeviceInterfaceDetail(dev_info, &didata, NULL, 0, &req_size, NULL); - - // (bizarre way of doing it) create a buffer large enough to hold the - // fixed-size detail struct components, and the variable string size - SP_DEVICE_INTERFACE_DETAIL_DATA *didetail = - (SP_DEVICE_INTERFACE_DETAIL_DATA*) new BYTE[req_size]; - _ASSERT(didetail); - didetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - // now actually get the detail struct - if(!SetupDiGetDeviceInterfaceDetail(dev_info, &didata, didetail, - req_size, &req_size, NULL)) { - WARN(_T("couldn't get devinterface info for %u"), index); - break; - } - - // open a shared handle to the device to query it (this will succeed even - // if the wiimote is already Connect()'ed) - DEEP_TRACE(_T(".. querying device %s"), didetail->DevicePath); - Handle = CreateFile(didetail->DevicePath, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, NULL); - if(Handle == INVALID_HANDLE_VALUE) { - DEEP_TRACE(_T(".... failed with err %x (probably harmless)."), - GetLastError()); - goto skip; - } - - // get the device attributes - HIDD_ATTRIBUTES attrib; - attrib.Size = sizeof(attrib); - if(HidD_GetAttributes(Handle, &attrib)) - { - // is this a wiimote? - if((attrib.VendorID != VID) || (attrib.ProductID != PID)) - goto skip; - - // yes, but is it the one we're interested in? - ++wiimotes_found; - if((wiimote_index != FIRST_AVAILABLE) && - (wiimote_index != wiimotes_found)) - goto skip; - - // the wiimote is installed, but it may not be currently paired: - if(wiimote_index == FIRST_AVAILABLE) - TRACE(_T(".. opening Wiimote %u:"), wiimotes_found); - else - TRACE(_T(".. opening:")); - - - // re-open the handle, but this time we don't allow write sharing - // (that way subsequent calls can still _discover_ wiimotes above, but - // will correctly fail here if they're already connected) - CloseHandle(Handle); - - // note this also means that if another application has already opened - // the device, the library can no longer connect it (this may happen - // with software that enumerates all joysticks in the system, because - // even though the wiimote is not a standard joystick (and can't - // be read as such), it unfortunately announces itself to the OS - // as one. The SDL library was known to do grab wiimotes like this. - // If you cannot stop the application from doing it, you may change the - // call below to open the device in full shared mode - but then the - // library can no longer detect if you've already connected a device - // and will allow you to connect it twice! So be careful ... - Handle = CreateFile(didetail->DevicePath, GENERIC_READ|GENERIC_WRITE, - FILE_SHARE_READ| FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - if(Handle == INVALID_HANDLE_VALUE) { - TRACE(_T(".... failed with err %x"), GetLastError()); - goto skip; - } - - // clear the wiimote state & buffers - Clear (false); // preserves existing deadzones - Internal.Clear(false); // " - InternalChanged = NO_CHANGE; - memset(ReadBuff , 0, sizeof(ReadBuff)); - bConnectionLost = false; - bConnectInProgress = true; // don't parse extensions or request regular - // updates until complete - // enable async reading - BeginAsyncRead(); - - // autodetect which write method the Bluetooth stack supports, - // by requesting the wiimote status report: - if(force_hidwrites && !_HidD_SetOutputReport) { - TRACE(_T(".. can't force HID writes (not supported)")); - force_hidwrites = false; - } - - if(force_hidwrites) - TRACE(_T(".. (HID writes forced)")); - else{ - // - try WriteFile() first as it's the most efficient (it uses - // harware interrupts where possible and is async-capable): - bUseHIDwrite = false; - RequestStatusReport(); - // and wait for the report to arrive: - DWORD last_time = timeGetTime(); - while(!bStatusReceived && ((timeGetTime()-last_time) < 500)) - Sleep(10); - TRACE(_T(".. WriteFile() %s."), bStatusReceived? _T("succeeded") : - _T("failed")); - } - - // try HID write method (if supported) - if(!bStatusReceived && _HidD_SetOutputReport) - { - bUseHIDwrite = true; - RequestStatusReport(); - // wait for the report to arrive: - DWORD last_time = timeGetTime(); - while(!bStatusReceived && ((timeGetTime()-last_time) < 500)) - Sleep(10); - // did we get it? - TRACE(_T(".. HID write %s."), bStatusReceived? _T("succeeded") : - _T("failed")); - } - - // still failed? - if(!bStatusReceived) { - WARN(_T("output failed - wiimote is not connected (or confused).")); - Disconnect(); - goto skip; - } - -//Sleep(500); - // reset it - Reset(); - - // read the wiimote calibration info - ReadCalibration(); - - // allow the result(s) to come in (so that the caller can immediately test - // MotionPlusConnected() - Sleep(300); // note, don't need it on my system, better to be safe though - - // connected succesfully: - _TotalConnected++; - - // use the first incomding analogue sensor values as the 'at rest' - // offsets (only supports the Balance Board currently) - bCalibrateAtRest = true; - - // refresh the public state from the internal one (so that everything - // is available straight away - RefreshState(); - - // attempt to construct a unique hardware ID from the calibration - // data bytes (this is obviously not guaranteed to be unique across - // all devices, but may work fairly well in practice... ?) - memcpy(&UniqueID, &CalibrationInfo, sizeof(CalibrationInfo)); - - _ASSERT(UniqueID != 0); // if this fires, the calibration data didn't - // arrive - this shouldn't happen - -#ifdef ID2_FROM_DEVICEPATH // (see comments in header) - // create a 2nd alternative id by simply adding all the characters - // in the device path to create a single number - UniqueID2 = 0; - for(unsigned index=0; index<_tcslen(didetail->DevicePath); index++) - UniqueID2 += didetail->DevicePath[index]; -#endif - // and show when we want to trigger the next periodic status request - // (for battery level and connection loss detection) - NextStatusTime = timeGetTime() + REQUEST_STATUS_EVERY_MS; - NextMPlusDetectTime = timeGetTime() + DETECT_MPLUS_EVERY_MS; - MPlusDetectCount = DETECT_MPLUS_COUNT; - - // tidy up - delete[] (BYTE*)didetail; - break; - } -skip: - // tidy up - delete[] (BYTE*)didetail; - - if(Handle != INVALID_HANDLE_VALUE) { - CloseHandle(Handle); - Handle = INVALID_HANDLE_VALUE; - } - // if this was the specified wiimote index, abort - if((wiimote_index != FIRST_AVAILABLE) && - (wiimote_index == (wiimotes_found-1))) - break; - - index++; - } - - // clean up our list - SetupDiDestroyDeviceInfoList(dev_info); - - bConnectInProgress = false; - if(IsConnected()) { - TRACE(_T(".. connected!")); - // notify the callbacks (if requested to do so) - if(CallbackTriggerFlags & CONNECTED) - { - ChangedNotifier(CONNECTED, Internal); - if(ChangedCallback) - ChangedCallback(*this, CONNECTED, Internal); - } - return true; - } - TRACE(_T(".. connection failed.")); - return false; - } -// ------------------------------------------------------------------------------------ -void wiimote::CalibrateAtRest () - { - _ASSERT(IsConnected()); - if(!IsConnected()) - return; - - // the app calls this to remove 'at rest' offsets from the analogue sensor - // values (currently only works for the Balance Board): - if(IsBalanceBoard()) { - TRACE(_T(".. removing 'at rest' BBoard offsets.")); - Internal.BalanceBoard.AtRestKg = Internal.BalanceBoard.Kg; - RefreshState(); - } - } -// ------------------------------------------------------------------------------------ -void wiimote::Disconnect () - { - if(Handle == INVALID_HANDLE_VALUE) - return; - - TRACE(_T("Disconnect().")); - - if(IsConnected()) - { - _ASSERT(_TotalConnected > 0); // sanity - _TotalConnected--; - - if(!bConnectionLost) - Reset(); - } - - CloseHandle(Handle); - Handle = INVALID_HANDLE_VALUE; - UniqueID = 0; -#ifdef ID2_FROM_DEVICEPATH // (see comments in header) - UniqueID2 = 0; -#endif - - // close the read thread - if(ReadParseThread) { - // unblock it so it can realise we're closing and exit straight away - SetEvent(DataRead); - WaitForSingleObject(ReadParseThread, 3000); - CloseHandle(ReadParseThread); - ReadParseThread = NULL; - } - // close the rumble thread - if(AsyncRumbleThread) { - WaitForSingleObject(AsyncRumbleThread, 3000); - CloseHandle(AsyncRumbleThread); - AsyncRumbleThread = NULL; - AsyncRumbleTimeout = 0; - } - // and the sample streaming thread - if(SampleThread) { - WaitForSingleObject(SampleThread, 3000); - CloseHandle(SampleThread); - SampleThread = NULL; - } - -#ifndef USE_DYNAMIC_HIDQUEUE - HID.Deallocate(); -#endif - - bStatusReceived = false; - - // and clear the state - Clear (false); // (preserves deadzones) - Internal.Clear(false); // " - InternalChanged = NO_CHANGE; - } -// ------------------------------------------------------------------------------------ -void wiimote::Reset () - { - TRACE(_T("Resetting wiimote.")); - - if(bMotionPlusEnabled) - DisableMotionPlus(); - - // stop updates (by setting report type to non-continuous, buttons-only) - if(IsBalanceBoard()) - SetReportType(IN_BUTTONS_BALANCE_BOARD, false); - else - SetReportType(IN_BUTTONS, false); - - SetRumble (false); - SetLEDs (0x00); -// MuteSpeaker (true); - EnableSpeaker(false); - - Sleep(150); // avoids loosing the extension calibration data on Connect() - } -// ------------------------------------------------------------------------------------ -unsigned __stdcall wiimote::ReadParseThreadfunc (void* param) - { - // this thread waits for the async ReadFile() to deliver data & parses it. - // it also requests periodic status updates, deals with connection loss - // and ends state recordings with a specific duration: - _ASSERT(param); - wiimote &remote = *(wiimote*)param; - OVERLAPPED &overlapped = remote.Overlapped; - unsigned exit_code = 0; // (success) - - while(1) - { - // wait until the overlapped read completes, or the timeout is reached: - DWORD wait = WaitForSingleObject(overlapped.hEvent, 500); - - // before we deal with the result, let's do some housekeeping: - - // if we were recently Disconect()ed, exit now - if(remote.Handle == INVALID_HANDLE_VALUE) { - DEEP_TRACE(_T("read thread: wiimote was disconnected")); - break; - } - // ditto if the connection was lost (eg. through a failed write) - if(remote.bConnectionLost) - { -connection_lost: - TRACE(_T("read thread: connection to wiimote was lost")); - remote.Disconnect(); - remote.InternalChanged = (state_change_flags) - (remote.InternalChanged | CONNECTION_LOST); - // report via the callback (if any) - if(remote.CallbackTriggerFlags & CONNECTION_LOST) - { - remote.ChangedNotifier(CONNECTION_LOST, remote.Internal); - if(remote.ChangedCallback) - remote.ChangedCallback(remote, CONNECTION_LOST, remote.Internal); - } - break; - } - - DWORD time = timeGetTime(); - // periodic events (but not if we're streaming audio, - // we don't want to cause a glitch) - if(remote.IsConnected() && !remote.bInitInProgress && - !remote.IsPlayingAudio()) - { - // status request due? - if(time > remote.NextStatusTime) - { -#ifdef BEEP_ON_PERIODIC_STATUSREFRESH - Beep(2000,50); -#endif - remote.RequestStatusReport(); - // and schedule the next one - remote.NextStatusTime = time + REQUEST_STATUS_EVERY_MS; - } - // motion plus detection due? - if(!remote.IsBalanceBoard() && -// !remote.bConnectInProgress && - !remote.bMotionPlusExtension && - (remote.Internal.ExtensionType != MOTION_PLUS) && - (remote.Internal.ExtensionType != PARTIALLY_INSERTED) && - (time > remote.NextMPlusDetectTime)) - { - remote.DetectMotionPlusExtensionAsync(); - // we try several times in quick succession before the next - // delay: - if(--remote.MPlusDetectCount == 0) { - remote.NextMPlusDetectTime = time + DETECT_MPLUS_EVERY_MS; - remote.MPlusDetectCount = DETECT_MPLUS_COUNT; -#ifdef _DEBUG - TRACE(_T("--")); -#endif - } - } - } - - // if we're state recording and have reached the specified duration, stop - if(remote.Recording.bEnabled && (remote.Recording.EndTimeMS != UNTIL_STOP) && - (time >= remote.Recording.EndTimeMS)) - remote.Recording.bEnabled = false; - - // now handle the wait result: - // did the wait time out? - if(wait == WAIT_TIMEOUT) { - DEEP_TRACE(_T("read thread: timed out")); - continue; // wait again - } - // did an error occurr? - if(wait != WAIT_OBJECT_0) { - DEEP_TRACE(_T("read thread: error waiting!")); - remote.bConnectionLost = true; - // deal with it straight away to avoid a longer delay - goto connection_lost; - } - - // data was received: -#ifdef BEEP_DEBUG_READS - Beep(500,1); -#endif - DWORD read = 0; - // get the data read result - GetOverlappedResult(remote.Handle, &overlapped, &read, TRUE); - // if we read data, parse it - if(read) { - DEEP_TRACE(_T("read thread: parsing data")); - remote.OnReadData(read); - } - else - DEEP_TRACE(_T("read thread: didn't get any data??")); - } - - TRACE(_T("(ending read thread)")); -#ifdef BEEP_DEBUG_READS - if(exit_code != 0) - Beep(200,1000); -#endif - return exit_code; - } -// ------------------------------------------------------------------------------------ -bool wiimote::BeginAsyncRead () - { - // (this is also called before we're fully connected) - if(Handle == INVALID_HANDLE_VALUE) - return false; - - DEEP_TRACE(_T(".. starting async read")); -#ifdef BEEP_DEBUG_READS - Beep(1000,1); -#endif - - DWORD read; - if (!ReadFile(Handle, ReadBuff, REPORT_LENGTH, &read, &Overlapped)) { - DWORD err = GetLastError(); - if(err != ERROR_IO_PENDING) { - DEEP_TRACE(_T(".... ** ReadFile() failed! **")); - return false; - } - } - - // launch the completion wait/callback thread - if(!ReadParseThread) { - ReadParseThread = (HANDLE)_beginthreadex(NULL, 0, ReadParseThreadfunc, - this, 0, NULL); - DEEP_TRACE(_T(".... creating read thread")); - _ASSERT(ReadParseThread); - if(!ReadParseThread) - return false; - SetThreadPriority(ReadParseThread, WORKER_THREAD_PRIORITY); - } - - // if ReadFile completed while we called, signal the thread to proceed - if(read) { - DEEP_TRACE(_T(".... got data right away")); - SetEvent(DataRead); - } - return true; - } -// ------------------------------------------------------------------------------------ -void wiimote::OnReadData (DWORD bytes_read) - { - _ASSERT(bytes_read == REPORT_LENGTH); - - // copy our input buffer - BYTE buff [REPORT_LENGTH]; - memcpy(buff, ReadBuff, bytes_read); - - // start reading again - BeginAsyncRead(); - - // parse it - ParseInput(buff); - } -// ------------------------------------------------------------------------------------ -void wiimote::SetReportType (input_report type, bool continuous) - { - _ASSERT(IsConnected()); - if(!IsConnected()) - return; - - // the balance board only uses one type of report - _ASSERT(!IsBalanceBoard() || type == IN_BUTTONS_BALANCE_BOARD); - if(IsBalanceBoard() && (type != IN_BUTTONS_BALANCE_BOARD)) - return; - -#ifdef TRACE - #define TYPE2NAME(_type) (type==_type)? _T(#_type) - const TCHAR* name = TYPE2NAME(IN_BUTTONS) : - TYPE2NAME(IN_BUTTONS_ACCEL_IR) : - TYPE2NAME(IN_BUTTONS_ACCEL_EXT) : - TYPE2NAME(IN_BUTTONS_ACCEL_IR_EXT) : - TYPE2NAME(IN_BUTTONS_BALANCE_BOARD) : - _T("(unknown??)"); - TRACE(_T("ReportType: %s (%s)"), name, (continuous? _T("continuous") : - _T("non-continuous"))); -#endif - ReportType = type; - - switch(type) - { - case IN_BUTTONS_ACCEL_IR: - EnableIR(wiimote_state::ir::EXTENDED); - break; - case IN_BUTTONS_ACCEL_IR_EXT: - EnableIR(wiimote_state::ir::BASIC); - break; - default: - DisableIR(); - break; - } - - BYTE buff [REPORT_LENGTH] = {0}; - buff[0] = OUT_TYPE; - buff[1] = (continuous ? 0x04 : 0x00) | GetRumbleBit(); - buff[2] = (BYTE)type; - WriteReport(buff); -// Sleep(15); - } -// ------------------------------------------------------------------------------------ -void wiimote::SetLEDs (BYTE led_bits) - { - _ASSERT(IsConnected()); - if(!IsConnected() || bInitInProgress) - return; - - _ASSERT(led_bits <= 0x0f); - led_bits &= 0xf; - - BYTE buff [REPORT_LENGTH] = {0}; - buff[0] = OUT_LEDs; - buff[1] = (led_bits<<4) | GetRumbleBit(); - WriteReport(buff); - - Internal.LED.Bits = led_bits; - } -// ------------------------------------------------------------------------------------ -void wiimote::SetRumble (bool on) - { - _ASSERT(IsConnected()); - if(!IsConnected()) - return; - - if(Internal.bRumble == on) - return; - - Internal.bRumble = on; - - // if we're streaming audio, we don't need to send a report (sending it makes - // the audio glitch, and the rumble bit is sent with every report anyway) - if(IsPlayingAudio()) - return; - - BYTE buff [REPORT_LENGTH] = {0}; - buff[0] = OUT_STATUS; - buff[1] = on? 0x01 : 0x00; - WriteReport(buff); - } -// ------------------------------------------------------------------------------------ -unsigned __stdcall wiimote::AsyncRumbleThreadfunc (void* param) - { - // auto-disables rumble after x milliseconds: - _ASSERT(param); - wiimote &remote = *(wiimote*)param; - - while(remote.IsConnected()) - { - if(remote.AsyncRumbleTimeout) - { - DWORD current_time = timeGetTime(); - if(current_time >= remote.AsyncRumbleTimeout) - { - if(remote.Internal.bRumble) - remote.SetRumble(false); - remote.AsyncRumbleTimeout = 0; - } - Sleep(1); - } - else - Sleep(4); - } - return 0; - } -// ------------------------------------------------------------------------------------ -void wiimote::RumbleForAsync (unsigned milliseconds) - { - // rumble for a fixed amount of time - _ASSERT(IsConnected()); - if(!IsConnected()) - return; - - SetRumble(true); - - // show how long thread should wait to disable rumble again - // (it it's currently rumbling it will just extend the time) - AsyncRumbleTimeout = timeGetTime() + milliseconds; - - // create the thread? - if(AsyncRumbleThread) - return; - - AsyncRumbleThread = (HANDLE)_beginthreadex(NULL, 0, AsyncRumbleThreadfunc, this, - 0, NULL); - _ASSERT(AsyncRumbleThread); - if(!AsyncRumbleThread) { - WARN(_T("couldn't create rumble thread!")); - return; - } - SetThreadPriority(AsyncRumbleThread, WORKER_THREAD_PRIORITY); - } -// ------------------------------------------------------------------------------------ -void wiimote::RequestStatusReport () - { - // (this can be called before we're fully connected) - _ASSERT(Handle != INVALID_HANDLE_VALUE); - if(Handle == INVALID_HANDLE_VALUE) - return; - - BYTE buff [REPORT_LENGTH] = {0}; - buff[0] = OUT_STATUS; - buff[1] = GetRumbleBit(); - WriteReport(buff); - } -// ------------------------------------------------------------------------------------ -bool wiimote::ReadAddress (int address, short size) - { - // asynchronous - BYTE buff [REPORT_LENGTH] = {0}; - buff[0] = OUT_READMEMORY; - buff[1] = (BYTE)(((address & 0xff000000) >> 24) | GetRumbleBit()); - buff[2] = (BYTE)( (address & 0x00ff0000) >> 16); - buff[3] = (BYTE)( (address & 0x0000ff00) >> 8); - buff[4] = (BYTE)( (address & 0x000000ff)); - buff[5] = (BYTE)( (size & 0xff00 ) >> 8); - buff[6] = (BYTE)( (size & 0xff)); - return WriteReport(buff); - } -// ------------------------------------------------------------------------------------ -void wiimote::WriteData (int address, BYTE size, const BYTE* buff) - { - // asynchronous - BYTE write [REPORT_LENGTH] = {0}; - write[0] = OUT_WRITEMEMORY; - write[1] = (BYTE)(((address & 0xff000000) >> 24) | GetRumbleBit()); - write[2] = (BYTE)( (address & 0x00ff0000) >> 16); - write[3] = (BYTE)( (address & 0x0000ff00) >> 8); - write[4] = (BYTE)( (address & 0x000000ff)); - write[5] = size; - memcpy(write+6, buff, size); - WriteReport(write); - } -// ------------------------------------------------------------------------------------ -int wiimote::ParseInput (BYTE* buff) - { - int changed = 0; - - // lock our internal state (so RefreshState() is blocked until we're done - EnterCriticalSection(&StateLock); - - switch(buff[0]) - { - case IN_BUTTONS: - DEEP_TRACE(_T(".. parsing buttons.")); - changed |= ParseButtons(buff); - break; - - case IN_BUTTONS_ACCEL: - DEEP_TRACE(_T(".. parsing buttons/accel.")); - changed |= ParseButtons(buff); - if(!IsBalanceBoard()) - changed |= ParseAccel(buff); - break; - - case IN_BUTTONS_ACCEL_EXT: - DEEP_TRACE(_T(".. parsing extenion/accel.")); - changed |= ParseButtons(buff); - changed |= ParseExtension(buff, 6); - if(!IsBalanceBoard()) - changed |= ParseAccel(buff); - break; - - case IN_BUTTONS_ACCEL_IR: - DEEP_TRACE(_T(".. parsing ir/accel.")); - changed |= ParseButtons(buff); - if(!IsBalanceBoard()) { - changed |= ParseAccel(buff); - changed |= ParseIR(buff); - } - break; - - case IN_BUTTONS_ACCEL_IR_EXT: - DEEP_TRACE(_T(".. parsing ir/extenion/accel.")); - changed |= ParseButtons(buff); - changed |= ParseExtension(buff, 16); - if(!IsBalanceBoard()) { - changed |= ParseAccel(buff); - changed |= ParseIR (buff); - } - break; - - case IN_BUTTONS_BALANCE_BOARD: - DEEP_TRACE(_T(".. parsing buttson/balance.")); - changed |= ParseButtons(buff); - changed |= ParseExtension(buff, 3); - break; - - case IN_READADDRESS: - DEEP_TRACE(_T(".. parsing read address.")); - changed |= ParseButtons (buff); - changed |= ParseReadAddress(buff); - break; - - case IN_STATUS: - DEEP_TRACE(_T(".. parsing status.")); - changed |= ParseStatus(buff); - // show that we received the status report (used for output method - // detection during Connect()) - bStatusReceived = true; - break; - - default: - DEEP_TRACE(_T(".. ** unknown input ** (happens).")); - ///_ASSERT(0); - //Debug.WriteLine("Unknown report type: " + type.ToString()); - LeaveCriticalSection(&StateLock); - return false; - } - - // if we're recording and some state we care about has changed, insert it into - // the state history - if(Recording.bEnabled && (changed & Recording.TriggerFlags)) - { - DEEP_TRACE(_T(".. adding state to history")); - state_event event; - event.time_ms = timeGetTime(); - event.state = *(wiimote_state*)this; - Recording.StateHistory->push_back(event); - } - - // for polling: show which state has changed since the last RefreshState() - InternalChanged = (state_change_flags)(InternalChanged | changed); - - LeaveCriticalSection(&StateLock); - - // callbacks: call it (if set & state the app is interested in has changed) - if(changed & CallbackTriggerFlags) - { - DEEP_TRACE(_T(".. calling state change callback")); - ChangedNotifier((state_change_flags)changed, Internal); - if(ChangedCallback) - ChangedCallback(*this, (state_change_flags)changed, Internal); - } - - DEEP_TRACE(_T(".. parse complete.")); - return true; - } -// ------------------------------------------------------------------------------------ -state_change_flags wiimote::RefreshState () - { - // nothing changed since the last call? - if(InternalChanged == NO_CHANGE) - return NO_CHANGE; - - // copy the internal state to our public data members: - // synchronise the interal state with the read/parse thread (we don't want - // values changing during the copy) - EnterCriticalSection(&StateLock); - - // remember which state changed since the last call - state_change_flags changed = InternalChanged; - - // preserve the application-set deadzones (if any) - joystick::deadzone nunchuk_deadzone = Nunchuk.Joystick.DeadZone; - joystick::deadzone classic_joyl_deadzone = ClassicController.JoystickL.DeadZone; - joystick::deadzone classic_joyr_deadzone = ClassicController.JoystickR.DeadZone; - - // copy the internal state to the public one - *(wiimote_state*)this = Internal; - InternalChanged = NO_CHANGE; - - // restore the application-set deadzones - Nunchuk.Joystick.DeadZone = nunchuk_deadzone; - ClassicController.JoystickL.DeadZone = classic_joyl_deadzone; - ClassicController.JoystickR.DeadZone = classic_joyr_deadzone; - - LeaveCriticalSection(&StateLock); - - return changed; - } -// ------------------------------------------------------------------------------------ -void wiimote::DetectMotionPlusExtensionAsync () - { -#ifdef _DEBUG - TRACE(_T("(looking for motion plus)")); -#endif - // show that we're expecting the result shortly - MotionPlusDetectCount++; - // MotionPLus reports at a different address than other extensions (until - // activated, when it maps itself into the usual extension registers), so - // try to detect it first: - ReadAddress(REGISTER_MOTIONPLUS_DETECT, 6); - } -// ------------------------------------------------------------------------------------ -bool wiimote::EnableMotionPlus () - { - _ASSERT(bMotionPlusDetected); - if(!bMotionPlusDetected) - return false; - if(bMotionPlusEnabled) - return true; - - TRACE(_T("Enabling Motion Plus:")); - - bMotionPlusExtension = false; - bInitInProgress = true; - bEnablingMotionPlus = true; - - // Initialize it: - WriteData(REGISTER_MOTIONPLUS_INIT , 0x55); -// Sleep(50); - // Enable it (this maps it to the standard extension port): - WriteData(REGISTER_MOTIONPLUS_ENABLE, 0x04); -// Sleep(50); -Sleep(500); - return true; - } -// ------------------------------------------------------------------------------------ -bool wiimote::DisableMotionPlus () - { - if(!bMotionPlusDetected || !bMotionPlusEnabled) - return false; - - TRACE(_T("Disabling Motion Plus:")); - - // disable it (this makes standard extensions visible again) - WriteData(REGISTER_EXTENSION_INIT1, 0x55); - return true; - } -// ------------------------------------------------------------------------------------ -void wiimote::InitializeExtension () - { - TRACE(_T("Initialising Extension.")); - // wibrew.org: The new way to initialize the extension is by writing 0x55 to - // 0x(4)A400F0, then writing 0x00 to 0x(4)A400FB. It works on all extensions, and - // makes the extension type bytes unencrypted. This means that you no longer have - // to decrypt the extension bytes using the transform listed above. - bInitInProgress = true; -_ASSERT(Internal.bExtension); - // only initialize if it's not a MotionPlus - if(!bEnablingMotionPlus) { - WriteData (REGISTER_EXTENSION_INIT1, 0x55); - WriteData (REGISTER_EXTENSION_INIT2, 0x00); - } - else - bEnablingMotionPlus = false; - - ReadAddress(REGISTER_EXTENSION_TYPE , 6); - } -// ------------------------------------------------------------------------------------ -int wiimote::ParseStatus (BYTE* buff) - { - // parse the buttons - int changed = ParseButtons(buff); - - // get the battery level - BYTE battery_raw = buff[6]; - if(Internal.BatteryRaw != battery_raw) - changed |= BATTERY_CHANGED; - Internal.BatteryRaw = battery_raw; - // it is estimated that ~200 is the maximum battery level - Internal.BatteryPercent = battery_raw / 2; - - // there is also a flag that shows if the battery is nearly empty - bool drained = buff[3] & 0x01; - if(drained != bBatteryDrained) - { - bBatteryDrained = drained; - if(drained) - changed |= BATTERY_DRAINED; - } - - // leds - BYTE leds = buff[3] >> 4; - if(leds != Internal.LED.Bits) - changed |= LEDS_CHANGED; - Internal.LED.Bits = leds; - - // don't handle extensions until a connection is complete -// if(bConnectInProgress) -// return changed; - - bool extension = ((buff[3] & 0x02) != 0); -// TRACE(_T("(extension = %s)"), (extension? _T("TRUE") : _T("false"))); - - if(extension != Internal.bExtension) - { - if(!Internal.bExtension) - { - TRACE(_T("Extension connected:")); - Internal.bExtension = true; - InitializeExtension(); - } - else{ - TRACE(_T("Extension disconnected.")); - Internal.bExtension = false; - Internal.ExtensionType = wiimote_state::NONE; - bMotionPlusEnabled = false; - bMotionPlusExtension = false; - bMotionPlusDetected = false; - bInitInProgress = false; - bEnablingMotionPlus = false; - changed |= EXTENSION_DISCONNECTED; - // renable reports -// SetReportType(ReportType); - } - } - - return changed; - } -// ------------------------------------------------------------------------------------ -int wiimote::ParseButtons (BYTE* buff) - { - int changed = 0; - -// WORD bits = *(WORD*)(buff+1); - WORD bits = *(WORD*)(buff+1) & Button.ALL; - - if(bits != Internal.Button.Bits) - changed |= BUTTONS_CHANGED; - Internal.Button.Bits = bits; - - return changed; - } -// ------------------------------------------------------------------------------------ -bool wiimote::EstimateOrientationFrom (wiimote_state::acceleration &accel) - { - // Orientation estimate from acceleration data (shared between wiimote and nunchuk) - // return true if the orientation was updated - - // assume the controller is stationary if the acceleration vector is near - // 1g for several updates (this may not always be correct) - float length_sq = square(accel.X) + square(accel.Y) + square(accel.Z); - - // TODO: as I'm comparing _squared_ length, I really need different - // min/max epsilons... - #define DOT(x1,y1,z1, x2,y2,z2) ((x1*x2) + (y1*y2) + (z1*z2)) - - static const float epsilon = 0.2f; - if((length_sq >= (1.f-epsilon)) && (length_sq <= (1.f+epsilon))) - { - if(++WiimoteNearGUpdates < 2) - return false; - - // wiimote seems to be stationary: normalize the current acceleration - // (ie. the assumed gravity vector) - float inv_len = 1.f / sqrt(length_sq); - float x = accel.X * inv_len; - float y = accel.Y * inv_len; - float z = accel.Z * inv_len; - - // copy the values - accel.Orientation.X = x; - accel.Orientation.Y = y; - accel.Orientation.Z = z; - - // and extract pitch & roll from them: - // (may not be optimal) - float pitch = -asin(y) * 57.2957795f; -// float roll = asin(x) * 57.2957795f; - float roll = atan2(x,z) * 57.2957795f; - if(z < 0) { - pitch = (y < 0)? 180 - pitch : -180 - pitch; - roll = (x < 0)? -180 - roll : 180 - roll; - } - - accel.Orientation.Pitch = pitch; - accel.Orientation.Roll = roll; - - // show that we just updated orientation - accel.Orientation.UpdateAge = 0; -#ifdef BEEP_ON_ORIENTATION_ESTIMATE - Beep(2000, 1); -#endif - return true; // updated - } - - // not updated this time: - WiimoteNearGUpdates = 0; - // age the last orientation update - accel.Orientation.UpdateAge++; - return false; - } -// ------------------------------------------------------------------------------------ -void wiimote::ApplyJoystickDeadZones (wiimote_state::joystick &joy) - { - // apply the deadzones to each axis (if set) - if((joy.DeadZone.X > 0.f) && (joy.DeadZone.X <= 1.f)) - { - if(fabs(joy.X) <= joy.DeadZone.X) - joy.X = 0; - else{ - joy.X -= joy.DeadZone.X * sign(joy.X); - joy.X /= 1.f - joy.DeadZone.X; - } - } - if((joy.DeadZone.Y > 0.f) && (joy.DeadZone.Y <= 1.f)) - { - if(fabs(joy.Y) <= joy.DeadZone.Y) - joy.Y = 0; - else{ - joy.Y -= joy.DeadZone.Y * sign(joy.Y); - joy.Y /= 1.f - joy.DeadZone.Y; - } - } - } -// ------------------------------------------------------------------------------------ -int wiimote::ParseAccel (BYTE* buff) - { - int changed = 0; - - BYTE raw_x = buff[3]; - BYTE raw_y = buff[4]; - BYTE raw_z = buff[5]; - - if((raw_x != Internal.Acceleration.RawX) || - (raw_y != Internal.Acceleration.RawY) || - (raw_z != Internal.Acceleration.RawZ)) - changed |= ACCEL_CHANGED; - - Internal.Acceleration.RawX = raw_x; - Internal.Acceleration.RawY = raw_y; - Internal.Acceleration.RawZ = raw_z; - - // avoid / 0.0 when calibration data hasn't arrived yet - if(Internal.CalibrationInfo.X0) - { - Internal.Acceleration.X = - ((float)Internal.Acceleration.RawX - Internal.CalibrationInfo.X0) / - ((float)Internal.CalibrationInfo.XG - Internal.CalibrationInfo.X0); - Internal.Acceleration.Y = - ((float)Internal.Acceleration.RawY - Internal.CalibrationInfo.Y0) / - ((float)Internal.CalibrationInfo.YG - Internal.CalibrationInfo.Y0); - Internal.Acceleration.Z = - ((float)Internal.Acceleration.RawZ - Internal.CalibrationInfo.Z0) / - ((float)Internal.CalibrationInfo.ZG - Internal.CalibrationInfo.Z0); - } - else{ - Internal.Acceleration.X = - Internal.Acceleration.Y = - Internal.Acceleration.Z = 0.f; - } - - // see if we can estimate the orientation from the current values - if(EstimateOrientationFrom(Internal.Acceleration)) - changed |= ORIENTATION_CHANGED; - - return changed; - } -// ------------------------------------------------------------------------------------ -int wiimote::ParseIR (BYTE* buff) - { - if(Internal.IR.Mode == wiimote_state::ir::OFF) - return NO_CHANGE; - - // avoid garbage values when the MotionPlus is enabled, but the app is - // still using the extended IR report type - if(bMotionPlusEnabled && (Internal.IR.Mode == wiimote_state::ir::EXTENDED)) - return NO_CHANGE; - - // take a copy of the existing IR state (so we can detect changes) - wiimote_state::ir prev_ir = Internal.IR; - - // only updates the other values if the dots are visible (so that the last - // valid values stay unmodified) - switch(Internal.IR.Mode) - { - case wiimote_state::ir::BASIC: - // 2 dots are encoded in 5 bytes, so read 2 at a time - for(unsigned step=0; step<2; step++) - { - ir::dot &dot0 = Internal.IR.Dot[step*2 ]; - ir::dot &dot1 = Internal.IR.Dot[step*2+1]; - const unsigned offs = 6 + (step*5); // 5 bytes for 2 dots - - dot0.bVisible = !(buff[offs ] == 0xff && buff[offs+1] == 0xff); - dot1.bVisible = !(buff[offs+3] == 0xff && buff[offs+4] == 0xff); - - if(dot0.bVisible) { - dot0.RawX = buff[offs ] | ((buff[offs+2] >> 4) & 0x03) << 8;; - dot0.RawY = buff[offs+1] | ((buff[offs+2] >> 6) & 0x03) << 8;; - dot0.X = 1.f - (dot0.RawX / (float)wiimote_state::ir::MAX_RAW_X); - dot0.Y = (dot0.RawY / (float)wiimote_state::ir::MAX_RAW_Y); - } - if(dot1.bVisible) { - dot1.RawX = buff[offs+3] | ((buff[offs+2] >> 0) & 0x03) << 8; - dot1.RawY = buff[offs+4] | ((buff[offs+2] >> 2) & 0x03) << 8; - dot1.X = 1.f - (dot1.RawX / (float)wiimote_state::ir::MAX_RAW_X); - dot1.Y = (dot1.RawY / (float)wiimote_state::ir::MAX_RAW_Y); - } - } - break; - - case wiimote_state::ir::EXTENDED: - // each dot is encoded into 3 bytes - for(unsigned index=0; index<4; index++) - { - ir::dot &dot = Internal.IR.Dot[index]; - const unsigned offs = 6 + (index * 3); - - dot.bVisible = !(buff[offs ]==0xff && buff[offs+1]==0xff && - buff[offs+2]==0xff); - if(dot.bVisible) { - dot.RawX = buff[offs ] | ((buff[offs+2] >> 4) & 0x03) << 8; - dot.RawY = buff[offs+1] | ((buff[offs+2] >> 6) & 0x03) << 8; - dot.X = 1.f - (dot.RawX / (float)wiimote_state::ir::MAX_RAW_X); - dot.Y = (dot.RawY / (float)wiimote_state::ir::MAX_RAW_Y); - dot.Size = buff[offs+2] & 0x0f; - } - } - break; - - case wiimote_state::ir::FULL: - _ASSERT(0); // not supported yet; - break; - } - - return memcmp(&prev_ir, &Internal.IR, sizeof(Internal.IR))? IR_CHANGED : 0; - } -// ------------------------------------------------------------------------------------ -inline float wiimote::GetBalanceValue (short sensor, short min, short mid, short max) - { - if(max == mid || mid == min) - return 0; - - float val = (sensor < mid)? - 68.0f * ((float)(sensor - min) / (mid - min)) : - 68.0f * ((float)(sensor - mid) / (max - mid)) + 68.0f; - - // divide by four (so that each sensor is correct) - return val * 0.25f; - } -// ------------------------------------------------------------------------------------ -int wiimote::ParseExtension (BYTE *buff, unsigned offset) - { - int changed = 0; - - switch(Internal.ExtensionType) - { - case wiimote_state::NUNCHUK: - { - // buttons - bool c = (buff[offset+5] & 0x02) == 0; - bool z = (buff[offset+5] & 0x01) == 0; - - if((c != Internal.Nunchuk.C) || (z != Internal.Nunchuk.Z)) - changed |= NUNCHUK_BUTTONS_CHANGED; - - Internal.Nunchuk.C = c; - Internal.Nunchuk.Z = z; - - // acceleration - { - wiimote_state::acceleration &accel = Internal.Nunchuk.Acceleration; - - BYTE raw_x = buff[offset+2]; - BYTE raw_y = buff[offset+3]; - BYTE raw_z = buff[offset+4]; - if((raw_x != accel.RawX) || (raw_y != accel.RawY) || (raw_z != accel.RawZ)) - changed |= NUNCHUK_ACCEL_CHANGED; - - accel.RawX = raw_x; - accel.RawY = raw_y; - accel.RawZ = raw_z; - - wiimote_state::nunchuk::calibration_info &calib = - Internal.Nunchuk.CalibrationInfo; - accel.X = ((float)raw_x - calib.X0) / ((float)calib.XG - calib.X0); - accel.Y = ((float)raw_y - calib.Y0) / ((float)calib.YG - calib.Y0); - accel.Z = ((float)raw_z - calib.Z0) / ((float)calib.ZG - calib.Z0); - - // try to extract orientation from the accel: - if(EstimateOrientationFrom(accel)) - changed |= NUNCHUK_ORIENTATION_CHANGED; - } - { - // joystick: - wiimote_state::joystick &joy = Internal.Nunchuk.Joystick; - - float raw_x = buff[offset+0]; - float raw_y = buff[offset+1]; - - if((raw_x != joy.RawX) || (raw_y != joy.RawY)) - changed |= NUNCHUK_JOYSTICK_CHANGED; - - joy.RawX = raw_x; - joy.RawY = raw_y; - - // apply the calibration data - wiimote_state::nunchuk::calibration_info &calib = - Internal.Nunchuk.CalibrationInfo; - if(Internal.Nunchuk.CalibrationInfo.MaxX != 0x00) - joy.X = ((float)raw_x - calib.MidX) / ((float)calib.MaxX - calib.MinX); - if(calib.MaxY != 0x00) - joy.Y = ((float)raw_y - calib.MidY) / ((float)calib.MaxY - calib.MinY); - - // i prefer the outputs to range -1 - +1 (note this also affects the - // deadzone calculations) - joy.X *= 2; joy.Y *= 2; - - // apply the public deadzones to the internal state (if set) - joy.DeadZone = Nunchuk.Joystick.DeadZone; - ApplyJoystickDeadZones(joy); - } - } - break; - - case wiimote_state::CLASSIC: - case wiimote_state::GH3_GHWT_GUITAR: - case wiimote_state::GHWT_DRUMS: - { - // buttons: - WORD bits = *(WORD*)(buff+offset+4); - bits = ~bits; // need to invert bits since 0 is down, and 1 is up - - if(bits != Internal.ClassicController.Button.Bits) - changed |= CLASSIC_BUTTONS_CHANGED; - - Internal.ClassicController.Button.Bits = bits; - - // joysticks: - wiimote_state::joystick &joyL = Internal.ClassicController.JoystickL; - wiimote_state::joystick &joyR = Internal.ClassicController.JoystickR; - - float l_raw_x = (float) (buff[offset+0] & 0x3f); - float l_raw_y = (float) (buff[offset+1] & 0x3f); - float r_raw_x = (float)((buff[offset+2] >> 7) | - ((buff[offset+1] & 0xc0) >> 5) | - ((buff[offset+0] & 0xc0) >> 3)); - float r_raw_y = (float) (buff[offset+2] & 0x1f); - - if((joyL.RawX != l_raw_x) || (joyL.RawY != l_raw_y)) - changed |= CLASSIC_JOYSTICK_L_CHANGED; - if((joyR.RawX != r_raw_x) || (joyR.RawY != r_raw_y)) - changed |= CLASSIC_JOYSTICK_R_CHANGED; - - joyL.RawX = l_raw_x; joyL.RawY = l_raw_y; - joyR.RawX = r_raw_x; joyR.RawY = r_raw_y; - - // apply calibration - wiimote_state::classic_controller::calibration_info &calib = - Internal.ClassicController.CalibrationInfo; - if(calib.MaxXL != 0x00) - joyL.X = (joyL.RawX - calib.MidXL) / ((float)calib.MaxXL - calib.MinXL); - if(calib.MaxYL != 0x00) - joyL.Y = (joyL.RawY - calib.MidYL) / ((float)calib.MaxYL - calib.MinYL); - if(calib.MaxXR != 0x00) - joyR.X = (joyR.RawX - calib.MidXR) / ((float)calib.MaxXR - calib.MinXR); - if(calib.MaxYR != 0x00) - joyR.Y = (joyR.RawY - calib.MidYR) / ((float)calib.MaxYR - calib.MinYR); - - // i prefer the joystick outputs to range -1 - +1 (note this also affects - // the deadzone calculations) - joyL.X *= 2; joyL.Y *= 2; joyR.X *= 2; joyR.Y *= 2; - - // apply the public deadzones to the internal state (if set) - joyL.DeadZone = ClassicController.JoystickL.DeadZone; - joyR.DeadZone = ClassicController.JoystickR.DeadZone; - ApplyJoystickDeadZones(joyL); - ApplyJoystickDeadZones(joyR); - - // triggers - BYTE raw_trigger_l = ((buff[offset+2] & 0x60) >> 2) | - (buff[offset+3] >> 5); - BYTE raw_trigger_r = buff[offset+3] & 0x1f; - - if((raw_trigger_l != Internal.ClassicController.RawTriggerL) || - (raw_trigger_r != Internal.ClassicController.RawTriggerR)) - changed |= CLASSIC_TRIGGERS_CHANGED; - - Internal.ClassicController.RawTriggerL = raw_trigger_l; - Internal.ClassicController.RawTriggerR = raw_trigger_r; - - if(calib.MaxTriggerL != 0x00) - Internal.ClassicController.TriggerL = - (float)Internal.ClassicController.RawTriggerL / - ((float)calib.MaxTriggerL - calib.MinTriggerL); - if(calib.MaxTriggerR != 0x00) - Internal.ClassicController.TriggerR = - (float)Internal.ClassicController.RawTriggerR / - ((float)calib.MaxTriggerR - calib.MinTriggerR); - } - break; - - case BALANCE_BOARD: - { - wiimote_state::balance_board::sensors_raw prev_raw = - Internal.BalanceBoard.Raw; - Internal.BalanceBoard.Raw.TopR = - (short)((short)buff[offset+0] << 8 | buff[offset+1]); - Internal.BalanceBoard.Raw.BottomR = - (short)((short)buff[offset+2] << 8 | buff[offset+3]); - Internal.BalanceBoard.Raw.TopL = - (short)((short)buff[offset+4] << 8 | buff[offset+5]); - Internal.BalanceBoard.Raw.BottomL = - (short)((short)buff[offset+6] << 8 | buff[offset+7]); - - if((Internal.BalanceBoard.Raw.TopL != prev_raw.TopL) || - (Internal.BalanceBoard.Raw.TopR != prev_raw.TopR) || - (Internal.BalanceBoard.Raw.BottomL != prev_raw.BottomL) || - (Internal.BalanceBoard.Raw.BottomR != prev_raw.BottomR)) - changed |= BALANCE_WEIGHT_CHANGED; - - Internal.BalanceBoard.Kg.TopL = - GetBalanceValue(Internal.BalanceBoard.Raw.TopL, - Internal.BalanceBoard.CalibrationInfo.Kg0 .TopL, - Internal.BalanceBoard.CalibrationInfo.Kg17.TopL, - Internal.BalanceBoard.CalibrationInfo.Kg34.TopL); - Internal.BalanceBoard.Kg.TopR = - GetBalanceValue(Internal.BalanceBoard.Raw.TopR, - Internal.BalanceBoard.CalibrationInfo.Kg0 .TopR, - Internal.BalanceBoard.CalibrationInfo.Kg17.TopR, - Internal.BalanceBoard.CalibrationInfo.Kg34.TopR); - Internal.BalanceBoard.Kg.BottomL = - GetBalanceValue(Internal.BalanceBoard.Raw.BottomL, - Internal.BalanceBoard.CalibrationInfo.Kg0 .BottomL, - Internal.BalanceBoard.CalibrationInfo.Kg17.BottomL, - Internal.BalanceBoard.CalibrationInfo.Kg34.BottomL); - Internal.BalanceBoard.Kg.BottomR = - GetBalanceValue(Internal.BalanceBoard.Raw.BottomR, - Internal.BalanceBoard.CalibrationInfo.Kg0 .BottomR, - Internal.BalanceBoard.CalibrationInfo.Kg17.BottomR, - Internal.BalanceBoard.CalibrationInfo.Kg34.BottomR); - - // uses these as the 'at rest' offsets? (immediately after Connect(), - // or if the app called CalibrateAtRest()) - if(bCalibrateAtRest) { - bCalibrateAtRest = false; - TRACE(_T(".. Auto-removing 'at rest' BBoard offsets.")); - Internal.BalanceBoard.AtRestKg = Internal.BalanceBoard.Kg; - } - - // remove the 'at rest' offsets - Internal.BalanceBoard.Kg.TopL -= BalanceBoard.AtRestKg.TopL; - Internal.BalanceBoard.Kg.TopR -= BalanceBoard.AtRestKg.TopR; - Internal.BalanceBoard.Kg.BottomL -= BalanceBoard.AtRestKg.BottomL; - Internal.BalanceBoard.Kg.BottomR -= BalanceBoard.AtRestKg.BottomR; - - // compute the average - Internal.BalanceBoard.Kg.Total = Internal.BalanceBoard.Kg.TopL + - Internal.BalanceBoard.Kg.TopR + - Internal.BalanceBoard.Kg.BottomL + - Internal.BalanceBoard.Kg.BottomR; - // and convert to Lbs - const float KG2LB = 2.20462262f; - Internal.BalanceBoard.Lb = Internal.BalanceBoard.Kg; - Internal.BalanceBoard.Lb.TopL *= KG2LB; - Internal.BalanceBoard.Lb.TopR *= KG2LB; - Internal.BalanceBoard.Lb.BottomL *= KG2LB; - Internal.BalanceBoard.Lb.BottomR *= KG2LB; - Internal.BalanceBoard.Lb.Total *= KG2LB; - } - break; - - case MOTION_PLUS: - { - bMotionPlusDetected = true; - bMotionPlusEnabled = true; - - short yaw = ((unsigned short)buff[offset+3] & 0xFC)<<6 | - (unsigned short)buff[offset+0]; - short pitch = ((unsigned short)buff[offset+5] & 0xFC)<<6 | - (unsigned short)buff[offset+2]; - short roll = ((unsigned short)buff[offset+4] & 0xFC)<<6 | - (unsigned short)buff[offset+1]; - - // we get one set of bogus values when the MotionPlus is disconnected, - // so ignore them - if((yaw != 0x3fff) || (pitch != 0x3fff) || (roll != 0x3fff)) - { - wiimote_state::motion_plus::sensors_raw &raw = Internal.MotionPlus.Raw; - - if((raw.Yaw != yaw) || (raw.Pitch != pitch) || (raw.Roll != roll)) - changed |= MOTIONPLUS_SPEED_CHANGED; - - raw.Yaw = yaw; - raw.Pitch = pitch; - raw.Roll = roll; - - // convert to float values - bool yaw_slow = (buff[offset+3] & 0x2) == 0x2; - bool pitch_slow = (buff[offset+3] & 0x1) == 0x1; - bool roll_slow = (buff[offset+4] & 0x2) == 0x2; - float y_scale = yaw_slow? 0.05f : 0.25f; - float p_scale = pitch_slow? 0.05f : 0.25f; - float r_scale = roll_slow? 0.05f : 0.25f; - - Internal.MotionPlus.Speed.Yaw = -(raw.Yaw - 0x1F7F) * y_scale; - Internal.MotionPlus.Speed.Pitch = -(raw.Pitch - 0x1F7F) * p_scale; - Internal.MotionPlus.Speed.Roll = -(raw.Roll - 0x1F7F) * r_scale; - - // show if there's an extension plugged into the MotionPlus: - bool extension = buff[offset+4] & 1; - if(extension != bMotionPlusExtension) - { - if(extension) { - TRACE(_T(".. MotionPlus extension found.")); - changed |= MOTIONPLUS_EXTENSION_CONNECTED; - } - else{ - TRACE(_T(".. MotionPlus' extension disconnected.")); - changed |= MOTIONPLUS_EXTENSION_DISCONNECTED; - } - } - bMotionPlusExtension = extension; - } - // while we're getting data, the plus is obviously detected/enabled -// bMotionPlusDetected = bMotionPlusEnabled = true; - } - break; - } - - return changed; - } -// ------------------------------------------------------------------------------------ -int wiimote::ParseReadAddress (BYTE* buff) - { - // decode the address that was queried: - int address = buff[4]<<8 | buff[5]; - int size = buff[3] >> 4; - int changed = 0; - - if((buff[3] & 0x08) != 0) { - WARN(_T("error: read address not valid.")); - _ASSERT(0); - return NO_CHANGE; - } - // address read failed (write-only)? - else if((buff[3] & 0x07) != 0) - { - // this also happens when attempting to detect a non-existant MotionPlus - if(MotionPlusDetectCount) - { - --MotionPlusDetectCount; - if(Internal.ExtensionType == MOTION_PLUS) - { - if(bMotionPlusDetected) - TRACE(_T(".. MotionPlus removed.")); - bMotionPlusDetected = false; - bMotionPlusEnabled = false; - // the MotionPlus can sometimes get confused - initializing - // extenions fixes it: -// if(address == 0xfa) -// InitializeExtension(); - } - } - else - WARN(_T("error: attempt to read from write-only register 0x%X."), buff[3]); - - return NO_CHANGE; - } - - // *NOTE*: this is a major (but convenient) hack! The returned data only - // contains the lower two bytes of the address that was queried. - // as these don't collide between any of the addresses/registers - // we currently read, it's OK to match just those two bytes - - // skip the header - buff += 6; - - switch(address) - { - case (REGISTER_CALIBRATION & 0xffff): - { - _ASSERT(size == 6); - TRACE(_T(".. got wiimote calibration.")); - Internal.CalibrationInfo.X0 = buff[0]; - Internal.CalibrationInfo.Y0 = buff[1]; - Internal.CalibrationInfo.Z0 = buff[2]; - Internal.CalibrationInfo.XG = buff[4]; - Internal.CalibrationInfo.YG = buff[5]; - Internal.CalibrationInfo.ZG = buff[6]; - //changed |= CALIBRATION_CHANGED; - } - break; - - // note: this covers both the normal extension and motion plus extension - // addresses (0x4a400fa / 0x4a600fa) - case (REGISTER_EXTENSION_TYPE & 0xffff): - { - _ASSERT(size == 5); - QWORD type = *(QWORD*)buff; - -// TRACE(_T("(found extension 0x%I64x)"), type); - - static const QWORD NUNCHUK = 0x000020A40000ULL; - static const QWORD CLASSIC = 0x010120A40000ULL; - static const QWORD GH3_GHWT_GUITAR = 0x030120A40000ULL; - static const QWORD GHWT_DRUMS = 0x030120A40001ULL; - static const QWORD BALANCE_BOARD = 0x020420A40000ULL; - static const QWORD MOTION_PLUS = 0x050420A40000ULL; - static const QWORD MOTION_PLUS_DETECT = 0x050020a60000ULL; - static const QWORD MOTION_PLUS_DETECT2 = 0x050420a60000ULL; - static const QWORD PARTIALLY_INSERTED = 0xffffffffffffULL; - - // MotionPlus: _before_ it's been activated - if((type == MOTION_PLUS_DETECT) || (type == MOTION_PLUS_DETECT2)) - { - if(!bMotionPlusDetected) { - TRACE(_T("Motion Plus detected!")); - changed |= MOTIONPLUS_DETECTED; - } - bMotionPlusDetected = true; - --MotionPlusDetectCount; - break; - } - - #define IF_TYPE(id) if(type == id) { \ - /* sometimes it comes in more than once */ \ - if(Internal.ExtensionType == wiimote_state::id)\ - break; \ - Internal.ExtensionType = wiimote_state::id; - - // MotionPlus: once it's activated & mapped to the standard ext. port - IF_TYPE(MOTION_PLUS) - TRACE(_T(".. Motion Plus!")); - // and start a query for the calibration data - ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16); - bMotionPlusDetected = true; - } - else IF_TYPE(NUNCHUK) - TRACE(_T(".. Nunchuk!")); - bMotionPlusEnabled = false; - // and start a query for the calibration data - ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16); - } - else IF_TYPE(CLASSIC) - TRACE(_T(".. Classic Controller!")); - bMotionPlusEnabled = false; - // and start a query for the calibration data - ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16); - } - else IF_TYPE(GH3_GHWT_GUITAR) - // sometimes it comes in more than once? - TRACE(_T(".. GH3/GHWT Guitar Controller!")); - bMotionPlusEnabled = false; - // and start a query for the calibration data - ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16); - } - else IF_TYPE(GHWT_DRUMS) - TRACE(_T(".. GHWT Drums!")); - bMotionPlusEnabled = false; - // and start a query for the calibration data - ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16); - } - else IF_TYPE(BALANCE_BOARD) - TRACE(_T(".. Balance Board!")); - bMotionPlusEnabled = false; - // and start a query for the calibration data - ReadAddress(REGISTER_BALANCE_CALIBRATION, 24); - } - else if(type == PARTIALLY_INSERTED) { - // sometimes it comes in more than once? - if(Internal.ExtensionType == wiimote_state::PARTIALLY_INSERTED) - Sleep(50); - TRACE(_T(".. partially inserted!")); - bMotionPlusEnabled = false; - Internal.ExtensionType = wiimote_state::PARTIALLY_INSERTED; - changed |= EXTENSION_PARTIALLY_INSERTED; - // try initializing the extension again by requesting another - // status report (this usually fixes it) - Internal.bExtension = false; - RequestStatusReport(); - } - else{ - TRACE(_T("unknown extension controller found (0x%I64x)"), type); - } - } - break; - - case (REGISTER_EXTENSION_CALIBRATION & 0xffff): - case (REGISTER_BALANCE_CALIBRATION & 0xffff): - { -// _ASSERT(((Internal.ExtensionType == BALANCE_BOARD) && (size == 31)) || -// ((Internal.ExtensionType != BALANCE_BOARD) && (size == 15))); - - switch(Internal.ExtensionType) - { - case wiimote_state::NUNCHUK: - { - wiimote_state::nunchuk::calibration_info - &calib = Internal.Nunchuk.CalibrationInfo; - - calib.X0 = buff[ 0]; - calib.Y0 = buff[ 1]; - calib.Z0 = buff[ 2]; - calib.XG = buff[ 4]; - calib.YG = buff[ 5]; - calib.ZG = buff[ 6]; - calib.MaxX = buff[ 8]; - calib.MinX = buff[ 9]; - calib.MidX = buff[10]; - calib.MaxY = buff[11]; - calib.MinY = buff[12]; - calib.MidY = buff[13]; - - changed |= NUNCHUK_CONNECTED;//|NUNCHUK_CALIBRATION_CHANGED; - // reenable reports -// SetReportType(ReportType); - } - break; - - case wiimote_state::CLASSIC: - case wiimote_state::GH3_GHWT_GUITAR: - case wiimote_state::GHWT_DRUMS: - { - wiimote_state::classic_controller::calibration_info - &calib = Internal.ClassicController.CalibrationInfo; - - calib.MaxXL = buff[ 0] >> 2; - calib.MinXL = buff[ 1] >> 2; - calib.MidXL = buff[ 2] >> 2; - calib.MaxYL = buff[ 3] >> 2; - calib.MinYL = buff[ 4] >> 2; - calib.MidYL = buff[ 5] >> 2; - calib.MaxXR = buff[ 6] >> 3; - calib.MinXR = buff[ 7] >> 3; - calib.MidXR = buff[ 8] >> 3; - calib.MaxYR = buff[ 9] >> 3; - calib.MinYR = buff[10] >> 3; - calib.MidYR = buff[11] >> 3; - // this doesn't seem right... - // calib.MinTriggerL = buff[12] >> 3; - // calib.MaxTriggerL = buff[14] >> 3; - // calib.MinTriggerR = buff[13] >> 3; - // calib.MaxTriggerR = buff[15] >> 3; - calib.MinTriggerL = 0; - calib.MaxTriggerL = 31; - calib.MinTriggerR = 0; - calib.MaxTriggerR = 31; - - changed |= CLASSIC_CONNECTED;//|CLASSIC_CALIBRATION_CHANGED; - // reenable reports -// SetReportType(ReportType); - } - break; - - case BALANCE_BOARD: - { - // first part, 0 & 17kg calibration values - wiimote_state::balance_board::calibration_info - &calib = Internal.BalanceBoard.CalibrationInfo; - - calib.Kg0 .TopR = (short)((short)buff[0] << 8 | buff[1]); - calib.Kg0 .BottomR = (short)((short)buff[2] << 8 | buff[3]); - calib.Kg0 .TopL = (short)((short)buff[4] << 8 | buff[5]); - calib.Kg0 .BottomL = (short)((short)buff[6] << 8 | buff[7]); - - calib.Kg17.TopR = (short)((short)buff[8] << 8 | buff[9]); - calib.Kg17.BottomR = (short)((short)buff[10] << 8 | buff[11]); - calib.Kg17.TopL = (short)((short)buff[12] << 8 | buff[13]); - calib.Kg17.BottomL = (short)((short)buff[14] << 8 | buff[15]); - - // 2nd part is scanned above - } - break; - - case MOTION_PLUS: - { - // TODO: not known how the calibration values work - changed |= MOTIONPLUS_ENABLED; - bMotionPlusEnabled = true; - bInitInProgress = false; - // reenable reports -// SetReportType(ReportType); - } - break; - } - case 0x34: - { - if(Internal.ExtensionType == BALANCE_BOARD) - { - wiimote_state::balance_board::calibration_info - &calib = Internal.BalanceBoard.CalibrationInfo; - - // 2nd part of the balance board calibration, - // 34kg calibration values - calib.Kg34.TopR = (short)((short)buff[0] << 8 | buff[1]); - calib.Kg34.BottomR = (short)((short)buff[2] << 8 | buff[3]); - calib.Kg34.TopL = (short)((short)buff[4] << 8 | buff[5]); - calib.Kg34.BottomL = (short)((short)buff[6] << 8 | buff[7]); - - changed |= BALANCE_CONNECTED; - // reenable reports - SetReportType(IN_BUTTONS_BALANCE_BOARD); - } - // else unknown what these are for - } - bInitInProgress = false; - } - break; - - default: -// _ASSERT(0); // shouldn't happen - break; - } - - return changed; - } -// ------------------------------------------------------------------------------------ -void wiimote::ReadCalibration () - { - TRACE(_T("Requestion wiimote calibration:")); - // this appears to change the report type to 0x31 - ReadAddress(REGISTER_CALIBRATION, 7); - } -// ------------------------------------------------------------------------------------ -void wiimote::EnableIR (wiimote_state::ir::mode mode) - { - Internal.IR.Mode = mode; - - BYTE buff [REPORT_LENGTH] = {0}; - buff[0] = OUT_IR; - buff[1] = 0x04 | GetRumbleBit(); - WriteReport(buff); - - memset(buff, 0, REPORT_LENGTH); - buff[0] = OUT_IR2; - buff[1] = 0x04 | GetRumbleBit(); - WriteReport(buff); - - static const BYTE ir_sens1[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, - 0xc0}; - static const BYTE ir_sens2[] = {0x40, 0x00}; - - WriteData(REGISTER_IR, 0x08); - Sleep(25); // wait a little to make IR more reliable (for some) - WriteData(REGISTER_IR_SENSITIVITY_1, sizeof(ir_sens1), ir_sens1); - WriteData(REGISTER_IR_SENSITIVITY_2, sizeof(ir_sens2), ir_sens2); - WriteData(REGISTER_IR_MODE, (BYTE)mode); - } -// ------------------------------------------------------------------------------------ -void wiimote::DisableIR () - { - Internal.IR.Mode = wiimote_state::ir::OFF; - - BYTE buff [REPORT_LENGTH] = {0}; - buff[0] = OUT_IR; - buff[1] = GetRumbleBit(); - WriteReport(buff); - - memset(buff, 0, REPORT_LENGTH); - buff[0] = OUT_IR2; - buff[1] = GetRumbleBit(); - WriteReport(buff); - } -// ------------------------------------------------------------------------------------ -unsigned __stdcall wiimote::HIDwriteThreadfunc (void* param) - { - _ASSERT(param); - TRACE(_T("(starting HID write thread)")); - wiimote &remote = *(wiimote*)param; - - while(remote.Handle != INVALID_HANDLE_VALUE) - { - // try to write the oldest entry in the queue -#ifdef USE_DYNAMIC_HIDQUEUE - if(!remote.HIDwriteQueue.empty()) -#else - if(!remote.HID.IsEmpty()) -#endif - { -#ifdef BEEP_DEBUG_WRITES - Beep(1500,1); -#endif - EnterCriticalSection(&remote.HIDwriteQueueLock); -#ifdef USE_DYNAMIC_HIDQUEUE - BYTE *buff = remote.HIDwriteQueue.front(); - _ASSERT(buff); -#else - BYTE *buff = remote.HID.Queue[remote.HID.ReadIndex].Report; -#endif - LeaveCriticalSection(&remote.HIDwriteQueueLock); - - if(!_HidD_SetOutputReport(remote.Handle, buff, REPORT_LENGTH)) - { - DWORD err = GetLastError(); -if(err==ERROR_BUSY) -TRACE(_T("**** HID WRITE: BUSY ****")); -else if(err == ERROR_NOT_READY) -TRACE(_T("**** HID WRITE: NOT READY ****")); - - if((err != ERROR_BUSY) && // "the requested resource is in use" - (err != ERROR_NOT_READY)) // "the device is not ready" - { - if(err == ERROR_NOT_SUPPORTED) { - WARN(_T("BT Stack doesn't suport HID writes!")); - goto remove_entry; - } - else{ - DEEP_TRACE(_T("HID write failed (err %u)! - "), err); - // if this worked previously, the connection was probably lost - if(remote.IsConnected()) - remote.bConnectionLost = true; - } - //_T("aborting write thread"), err); - //return 911; - } - } - else{ -remove_entry: - EnterCriticalSection(&remote.HIDwriteQueueLock); -#ifdef USE_DYNAMIC_HIDQUEUE - remote.HIDwriteQueue.pop(); - delete[] buff; -#else - remote.HID.ReadIndex++; - remote.HID.ReadIndex &= (hid::MAX_QUEUE_ENTRIES-1); -#endif - LeaveCriticalSection(&remote.HIDwriteQueueLock); - } - } - Sleep(1); - } - - TRACE(_T("ending HID write thread")); - return 0; - } -// ------------------------------------------------------------------------------------ -bool wiimote::WriteReport (BYTE *buff) - { -#ifdef BEEP_DEBUG_WRITES - Beep(2000,1); -#endif - -#ifdef _DEBUG - #define DEEP_TRACE_TYPE(type) case OUT_##type: DEEP_TRACE(_T("WriteReport: ")\ - _T(#type)); break - switch(buff[0]) - { - DEEP_TRACE_TYPE(NONE); - DEEP_TRACE_TYPE(LEDs); - DEEP_TRACE_TYPE(TYPE); - DEEP_TRACE_TYPE(IR); - DEEP_TRACE_TYPE(SPEAKER_ENABLE); - DEEP_TRACE_TYPE(STATUS); - DEEP_TRACE_TYPE(WRITEMEMORY); - DEEP_TRACE_TYPE(READMEMORY); - DEEP_TRACE_TYPE(SPEAKER_DATA); - DEEP_TRACE_TYPE(SPEAKER_MUTE); - DEEP_TRACE_TYPE(IR2); - default: - TRACE(_T("WriteReport: type [%02x][%02x]"), buff[1], buff[2]); - } -#endif - - if(bUseHIDwrite) - { - // HidD_SetOutputReport: +: works on MS Bluetooth stacks (WriteFile doesn't). - // -: is synchronous, so make it async - if(!HIDwriteThread) - { - HIDwriteThread = (HANDLE)_beginthreadex(NULL, 0, HIDwriteThreadfunc, - this, 0, NULL); - _ASSERT(HIDwriteThread); - if(!HIDwriteThread) { - WARN(_T("couldn't create HID write thread!")); - return false; - } - SetThreadPriority(HIDwriteThread, WORKER_THREAD_PRIORITY); - } - - // insert the write request into the thread's queue -#ifdef USE_DYNAMIC_HIDQUEUE - EnterCriticalSection(&HIDwriteQueueLock); - BYTE *buff_copy = new BYTE[REPORT_LENGTH]; -#else - // allocate the HID write queue once - if(!HID.Queue && !HID.Allocate()) - return false; - - EnterCriticalSection(&HIDwriteQueueLock); - BYTE *buff_copy = HID.Queue[HID.WriteIndex].Report; -#endif - memcpy(buff_copy, buff, REPORT_LENGTH); - -#ifdef USE_DYNAMIC_HIDQUEUE - HIDwriteQueue.push(buff_copy); -#else - HID.WriteIndex++; - HID.WriteIndex &= (HID.MAX_QUEUE_ENTRIES-1); - - // check if the fixed report queue has overflown: - // if this ASSERT triggers, the HID write queue (that stores reports - // for asynchronous output by HIDwriteThreadfunc) has overflown. - // this can happen if the connection with the wiimote has been lost - // and in that case is harmless. - // - // if it happens during normal operation though you need to increase - // hid::MAX_QUEUE_ENTRIES to the next power-of-2 (see comments) - // _and_ email me the working setting so I can update the next release - _ASSERT(HID.WriteIndex != HID.ReadIndex); -#endif - LeaveCriticalSection(&HIDwriteQueueLock); - return true; - } - - // WriteFile: - DWORD written; - if(!WriteFile(Handle, buff, REPORT_LENGTH, &written, &Overlapped)) - { - DWORD error = GetLastError(); - if(error != ERROR_IO_PENDING) { - TRACE(_T("WriteFile failed, err: %u!"), error); - // if it worked previously, assume we lost the connection - if(IsConnected()) - bConnectionLost = true; -#ifndef USE_DYNAMIC_HIDQUEUE - HID.Deallocate(); -#endif - return false; - } - } - return true; - } -// ------------------------------------------------------------------------------------ -// experimental speaker support: -// ------------------------------------------------------------------------------------ -bool wiimote::MuteSpeaker (bool on) - { - _ASSERT(IsConnected()); - if(!IsConnected()) - return false; - - if(Internal.Speaker.bMuted == on) - return true; - - if(on) TRACE(_T("muting speaker." )); - else TRACE(_T("unmuting speaker.")); - - BYTE buff [REPORT_LENGTH] = {0}; - buff[0] = OUT_SPEAKER_MUTE; - buff[1] = (on? 0x04 : 0x00) | GetRumbleBit(); - if(!WriteReport(buff)) - return false; - Sleep(1); - Internal.Speaker.bMuted = on; - return true; - } -// ------------------------------------------------------------------------------------ -bool wiimote::EnableSpeaker (bool on) - { - _ASSERT(IsConnected()); - if(!IsConnected()) - return false; - - if(Internal.Speaker.bEnabled == on) - return true; - - if(on) TRACE(_T("enabling speaker.")); else TRACE(_T("disabling speaker.")); - - BYTE buff [REPORT_LENGTH] = {0}; - buff[0] = OUT_SPEAKER_ENABLE; - buff[1] = (on? 0x04 : 0x00) | GetRumbleBit(); - if(!WriteReport(buff)) - return false; - - if(!on) { - Internal.Speaker.Freq = FREQ_NONE; - Internal.Speaker.Volume = 0; - MuteSpeaker(true); - } - - Internal.Speaker.bEnabled = on; - return true; - } -// ------------------------------------------------------------------------------------ -#ifdef TR4 // TEMP, ignore - extern int hzinc; -#endif -// ------------------------------------------------------------------------------------ -unsigned __stdcall wiimote::SampleStreamThreadfunc (void* param) - { - TRACE(_T("(starting sample thread)")); - // sends a simple square wave sample stream - wiimote &remote = *(wiimote*)param; - - static BYTE squarewave_report[REPORT_LENGTH] = - { OUT_SPEAKER_DATA, 20<<3, 0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, - 0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, }; - static BYTE sample_report [REPORT_LENGTH] = - { OUT_SPEAKER_DATA, 0 }; - - bool last_playing = false; - DWORD frame = 0; - DWORD frame_start = 0; - unsigned total_samples = 0; - unsigned sample_index = 0; - wiimote_sample *current_sample = NULL; - - // TODO: duration!! - while(remote.IsConnected()) - { - bool playing = remote.IsPlayingAudio(); - - if(!playing) - Sleep(1); - else{ - const unsigned freq_hz = FreqLookup[remote.Internal.Speaker.Freq]; -#ifdef TR4 - const float frame_ms = 1000 / ((freq_hz+hzinc) / 40.f); // 20bytes = 40 samples per write -#else - const float frame_ms = 1000 / (freq_hz / 40.f); // 20bytes = 40 samples per write -#endif - - // has the sample just changed? - bool sample_changed = (current_sample != remote.CurrentSample); - current_sample = (wiimote_sample*)remote.CurrentSample; - -// (attempts to minimise glitches, doesn't seem to help though) -//#define FIRSTFRAME_IS_SILENT // send all-zero for first frame - -#ifdef FIRSTFRAME_IS_SILENT - bool silent_frame = false; -#endif - if(!last_playing || sample_changed) { - frame = 0; - frame_start = timeGetTime(); - total_samples = current_sample? current_sample->length : 0; - sample_index = 0; -#ifdef FIRSTFRAME_IS_SILENT - silent_frame = true; -#endif - } - - // are we streaming a sample? - if(current_sample) - { - if(sample_index < current_sample->length) - { - // (remember that samples are 4bit, ie. 2 per byte) - unsigned samples_left = (current_sample->length - sample_index); - unsigned report_samples = min(samples_left, (unsigned)40); - // round the entries up to the nearest multiple of 2 - unsigned report_entries = (report_samples+1) >> 1; - - sample_report[1] = (BYTE)((report_entries<<3) | - remote.GetRumbleBit()); -#ifdef FIRSTFRAME_IS_SILENT - if(silent_frame) { - // send all-zeroes - for(unsigned index=0; indexsamples[(sample_index>>1)+index]; - remote.WriteReport(sample_report); - sample_index += report_samples; - } - } - else{ - // we reached the sample end - remote.CurrentSample = NULL; - current_sample = NULL; - remote.Internal.Speaker.Freq = FREQ_NONE; - remote.Internal.Speaker.Volume = 0; - } - } - // no, a squarewave - else{ - squarewave_report[1] = (20<<3) | remote.GetRumbleBit(); - remote.WriteReport(squarewave_report); -#if 0 - // verify that we're sending at the correct rate (we are) - DWORD elapsed = (timeGetTime()-frame_start); - unsigned total_samples = frame * 40; - float elapsed_secs = elapsed / 1000.f; - float sent_persec = total_samples / elapsed_secs; -#endif - } - - frame++; - - // send the first two buffers immediately? (attempts to lessen startup - // startup glitches by assuming we're filling a small sample - // (or general input) buffer on the wiimote) - doesn't seem to help -// if(frame > 2) { - while((timeGetTime()-frame_start) < (unsigned)(frame*frame_ms)) - Sleep(1); -// } - } - - last_playing = playing; - } - - TRACE(_T("(ending sample thread)")); - return 0; - } -// ------------------------------------------------------------------------------------ -bool wiimote::Load16bitMonoSampleWAV (const TCHAR* filepath, wiimote_sample &out) - { - // converts unsigned 16bit mono .wav audio data to the 4bit ADPCM variant - // used by the Wiimote (at least the closest match so far), and returns - // the data in a BYTE array (caller must delete[] it when no longer needed): - memset(&out, 0, sizeof(out)); - - TRACE(_T("Loading '%s'"), filepath); - - FILE *file; -#if (_MSC_VER >= 1400) // VC 2005+ - _tfopen_s(&file, filepath, _T("rb")); -#else - file = _tfopen(filepath, _T("rb")); -#endif - _ASSERT(file); - if(!file) { - WARN(_T("Couldn't open '%s"), filepath); - return false; - } - - // parse the .wav file - struct riff_chunkheader { - char ckID [4]; - DWORD ckSize; - char formType [4]; - }; - struct chunk_header { - char ckID [4]; - DWORD ckSize; - }; - union { - WAVEFORMATEX x; - WAVEFORMATEXTENSIBLE xe; - } wf = {0}; - - riff_chunkheader riff_chunkheader; - chunk_header chunk_header; - speaker_freq freq = FREQ_NONE; - - #define READ(data) if(fread(&data, sizeof(data), 1, file) != 1) { \ - TRACE(_T(".wav file corrupt")); \ - fclose(file); \ - return false; \ - } - #define READ_SIZE(ptr,size) if(fread(ptr, size, 1, file) != 1) { \ - TRACE(_T(".wav file corrupt")); \ - fclose(file); \ - return false; \ - } - // read the riff chunk header - READ(riff_chunkheader); - - // valid RIFF file? - _ASSERT(!strncmp(riff_chunkheader.ckID, "RIFF", 4)); - if(strncmp(riff_chunkheader.ckID, "RIFF", 4)) - goto unsupported; // nope - // valid WAV variant? - _ASSERT(!strncmp(riff_chunkheader.formType, "WAVE", 4)); - if(strncmp(riff_chunkheader.formType, "WAVE", 4)) - goto unsupported; // nope - - // find the format & data chunks - while(1) - { - READ(chunk_header); - - if(!strncmp(chunk_header.ckID, "fmt ", 4)) - { - // not a valid .wav file? - if(chunk_header.ckSize < 16 || - chunk_header.ckSize > sizeof(WAVEFORMATEXTENSIBLE)) - goto unsupported; - - READ_SIZE((BYTE*)&wf.x, chunk_header.ckSize); - - // now we know it's true wav file - bool extensible = (wf.x.wFormatTag == WAVE_FORMAT_EXTENSIBLE); - int format = extensible? wf.xe.SubFormat.Data1 : - wf.x .wFormatTag; - // must be uncompressed PCM (the format comparisons also work on - // the 'extensible' header, even though they're named differently) - if(format != WAVE_FORMAT_PCM) { - TRACE(_T(".. not uncompressed PCM")); - goto unsupported; - } - - // must be mono, 16bit - if((wf.x.nChannels != 1) || (wf.x.wBitsPerSample != 16)) { - TRACE(_T(".. %d bit, %d channel%s"), wf.x.wBitsPerSample, - wf.x.nChannels, - (wf.x.nChannels>1? _T("s"):_T(""))); - goto unsupported; - } - - // must be _near_ a supported speaker frequency range (but allow some - // tolerance, especially as the speaker freq values aren't final yet): - unsigned sample_freq = wf.x.nSamplesPerSec; - const unsigned epsilon = 100; // for now - - for(unsigned index=1; index= FreqLookup[index] && - (sample_freq-epsilon) <= FreqLookup[index]) { - freq = (speaker_freq)index; - TRACE(_T(".. using speaker freq %u"), FreqLookup[index]); - break; - } - } - if(freq == FREQ_NONE) { - WARN(_T("Couldn't (loosely) match .wav samplerate %u Hz to speaker"), - sample_freq); - goto unsupported; - } - } - else if(!strncmp(chunk_header.ckID, "data", 4)) - { - // make sure we got a valid fmt chunk first - if(!wf.x.nBlockAlign) - goto corrupt_file; - - // grab the data - unsigned total_samples = chunk_header.ckSize / wf.x.nBlockAlign; - if(total_samples == 0) - goto corrupt_file; - - short *samples = new short[total_samples]; - size_t read = fread(samples, 2, total_samples, file); - fclose(file); - if(read != total_samples) - { - if(read == 0) { - delete[] samples; - goto corrupt_file; - } - // got a different number, but use them anyway - WARN(_T("found %s .wav audio data than expected (%u/%u samples)"), - ((read < total_samples)? _T("less") : _T("more")), - read, total_samples); - - total_samples = read; - } - - // and convert them - bool res = Convert16bitMonoSamples(samples, true, total_samples, freq, - out); - delete[] samples; - return res; - } - else{ - // unknown chunk, skip its data - DWORD chunk_bytes = (chunk_header.ckSize + 1) & ~1L; - if(fseek(file, chunk_bytes, SEEK_CUR)) - goto corrupt_file; - } - } - -corrupt_file: - WARN(_T(".wav file is corrupt")); - fclose(file); - return false; - -unsupported: - WARN(_T(".wav file format not supported (must be mono 16bit PCM)")); - fclose(file); - return false; - } -// ------------------------------------------------------------------------------------ -bool wiimote::Load16BitMonoSampleRAW (const TCHAR* filepath, - bool _signed, - speaker_freq freq, - wiimote_sample &out) - { - // converts (.wav style) unsigned 16bit mono raw data to the 4bit ADPCM variant - // used by the Wiimote, and returns the data in a BYTE array (caller must - // delete[] it when no longer needed): - memset(&out, 0, sizeof(out)); - - // get the length of the file - struct _stat file_info; - if(_tstat(filepath, &file_info)) { - WARN(_T("couldn't get filesize for '%s'"), filepath); - return false; - } - - DWORD len = file_info.st_size; - _ASSERT(len); - if(!len) { - WARN(_T("zero-size sample file '%s'"), filepath); - return false; - } - - unsigned total_samples = (len+1) / 2; // round up just in case file is corrupt - // allocate a buffer to hold the samples to convert - short *samples = new short[total_samples]; - _ASSERT(samples); - if(!samples) { - TRACE(_T("Couldn't open '%s"), filepath); - return false; - } - - // load them - FILE *file; - bool res; -#if (_MSC_VER >= 1400) // VC 2005+ - _tfopen_s(&file, filepath, _T("rb")); -#else - file = _tfopen(filepath, _T("rb")); -#endif - _ASSERT(file); - if(!file) { - TRACE(_T("Couldn't open '%s"), filepath); - goto error; - } - - res = (fread(samples, 1, len, file) == len); - fclose(file); - if(!res) { - WARN(_T("Couldn't load file '%s'"), filepath); - goto error; - } - - // and convert them - res = Convert16bitMonoSamples(samples, _signed, total_samples, freq, out); - delete[] samples; - return res; - -error: - delete[] samples; - return false; - } -// ------------------------------------------------------------------------------------ -bool wiimote::Convert16bitMonoSamples (const short* samples, - bool _signed, - DWORD length, - speaker_freq freq, - wiimote_sample &out) - { - // converts 16bit mono sample data to the native 4bit format used by the Wiimote, - // and returns the data in a BYTE array (caller must delete[] when no - // longer needed): - memset(&out, 0, sizeof(0)); - - _ASSERT(samples && length); - if(!samples || !length) - return false; - - // allocate the output buffer - out.samples = new BYTE[length]; - _ASSERT(out.samples); - if(!out.samples) - return false; - - // clear it - memset(out.samples, 0, length); - out.length = length; - out.freq = freq; - - // ADPCM code, adapted from - // http://www.wiindows.org/index.php/Talk:Wiimote#Input.2FOutput_Reports - static const int index_table[16] = { -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 }; - static const int diff_table [16] = { 1, 3, 5, 7, 9, 11, 13, 15, - -1, -3, -5, -7, -9, -11, -13, 15 }; - static const int step_scale [16] = { 230, 230, 230, 230, 307, 409, 512, 614, - 230, 230, 230, 230, 307, 409, 512, 614 }; - // Encode to ADPCM, on initialization set adpcm_prev_value to 0 and adpcm_step - // to 127 (these variables must be preserved across reports) - int adpcm_prev_value = 0; - int adpcm_step = 127; - - for(size_t i=0; i 7) - diff = 7; - encoded_val |= diff; - adpcm_prev_value += ((adpcm_step * diff_table[encoded_val]) / 8); - if(adpcm_prev_value > 0x7fff) - adpcm_prev_value = 0x7fff; - if(adpcm_prev_value < -0x8000) - adpcm_prev_value = -0x8000; - adpcm_step = (adpcm_step * step_scale[encoded_val]) >> 8; - if(adpcm_step < 127) - adpcm_step = 127; - if(adpcm_step > 24567) - adpcm_step = 24567; - if(i & 1) - out.samples[i>>1] |= encoded_val; - else - out.samples[i>>1] |= encoded_val << 4; - } - - return true; - } -// ------------------------------------------------------------------------------------ -bool wiimote::PlaySample (const wiimote_sample &sample, BYTE volume, - speaker_freq freq_override) - { - _ASSERT(IsConnected()); - if(!IsConnected()) - return false; - - speaker_freq freq = freq_override? freq_override : sample.freq; - - TRACE(_T("playing sample.")); - EnableSpeaker(true); - MuteSpeaker (true); - -#if 0 - // combine everything into one write - faster, seems to work? - BYTE bytes[9] = { 0x00, 0x00, 0x00, 10+freq, vol, 0x00, 0x00, 0x01, 0x01 }; - WriteData(0x04a20001, sizeof(bytes), bytes); -#else - // Write 0x01 to register 0x04a20009 - WriteData(0x04a20009, 0x01); - // Write 0x08 to register 0x04a20001 - WriteData(0x04a20001, 0x08); - // Write 7-byte configuration to registers 0x04a20001-0x04a20008 - BYTE bytes[7] = { 0x00, 0x00, 0x00, 10+(BYTE)freq, volume, 0x00, 0x00 }; - WriteData(0x04a20001, sizeof(bytes), bytes); - // + Write 0x01 to register 0x04a20008 - WriteData(0x04a20008, 0x01); -#endif - - Internal.Speaker.Freq = freq; - Internal.Speaker.Volume = volume; - CurrentSample = &sample; - - MuteSpeaker(false); - - return StartSampleThread(); - } -// ------------------------------------------------------------------------------------ -bool wiimote::StartSampleThread () - { - if(SampleThread) - return true; - - SampleThread = (HANDLE)_beginthreadex(NULL, 0, SampleStreamThreadfunc, - this, 0, NULL); - _ASSERT(SampleThread); - if(!SampleThread) { - WARN(_T("couldn't create sample thread!")); - MuteSpeaker (true); - EnableSpeaker(false); - return false; - } - SetThreadPriority(SampleThread, WORKER_THREAD_PRIORITY); - return true; - } -// ------------------------------------------------------------------------------------ -bool wiimote::PlaySquareWave (speaker_freq freq, BYTE volume) - { - _ASSERT(IsConnected()); - if(!IsConnected()) - return false; - - // if we're already playing a sample, stop it first - if(IsPlayingSample()) - CurrentSample = NULL; - // if we're already playing a square wave at this freq and volume, return - else if(IsPlayingAudio() && (Internal.Speaker.Freq == freq) && - (Internal.Speaker.Volume == volume)) - return true; - - TRACE(_T("playing square wave.")); - // stop playing samples - CurrentSample = 0; - - EnableSpeaker(true); - MuteSpeaker (true); - -#if 0 - // combined everything into one write - much faster, seems to work? - BYTE bytes[9] = { 0x00, 0x00, 0x00, freq, volume, 0x00, 0x00, 0x01, 0x1 }; - WriteData(0x04a20001, sizeof(bytes), bytes); -#else - // write 0x01 to register 0xa20009 - WriteData(0x04a20009, 0x01); - // write 0x08 to register 0xa20001 - WriteData(0x04a20001, 0x08); - // write default sound mode (4bit ADPCM, we assume) 7-byte configuration - // to registers 0xa20001-0xa20008 - BYTE bytes[7] = { 0x00, 0x00, 0x00, 10+(BYTE)freq, volume, 0x00, 0x00 }; - WriteData(0x04a20001, sizeof(bytes), bytes); - // write 0x01 to register 0xa20008 - WriteData(0x04a20008, 0x01); -#endif - - Internal.Speaker.Freq = freq; - Internal.Speaker.Volume = volume; - - MuteSpeaker(false); - return StartSampleThread(); - } -// ------------------------------------------------------------------------------------ -void wiimote::RecordState (state_history &events_out, - unsigned max_time_ms, - state_change_flags change_trigger) - { - // user being naughty? - if(Recording.bEnabled) - StopRecording(); - - // clear the list - if(!events_out.empty()) - events_out.clear(); - - // start recording - Recording.StateHistory = &events_out; - Recording.StartTimeMS = timeGetTime(); - Recording.EndTimeMS = Recording.StartTimeMS + max_time_ms; - Recording.TriggerFlags = change_trigger; - // as this call happens outside the read/parse thread, set the boolean - // which will enable reocrding last, so that all params are in place. - // TODO: * stricly speaking this only works on VC2005+ or better, as it - // automatically places a memory barrier on volatile variables - earlier/ - // other compilers may reorder the assignments!). * - Recording.bEnabled = true; - } -// ------------------------------------------------------------------------------------ -void wiimote::StopRecording () - { - if(!Recording.bEnabled) - return; - - Recording.bEnabled = false; - // make sure the read/parse thread has time to notice the change (else it might - // still write one more state to the list) - Sleep(10); // too much? - } -// ------------------------------------------------------------------------------------ -// ------------------------------------------------------------------------------------ diff --git a/tracker-pt/wiiyourself/wiimote.h b/tracker-pt/wiiyourself/wiimote.h deleted file mode 100644 index 1db2c098..00000000 --- a/tracker-pt/wiiyourself/wiimote.h +++ /dev/null @@ -1,495 +0,0 @@ -// _______________________________________________________________________________ -// -// - 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) - -#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 - -#ifndef QWORD - typedef unsigned __int64 QWORD; -#endif - -#ifdef _MSC_VER // VC-specific: _DEBUG build only _ASSERT() sanity checks -# include -#elif defined(__MINGW32__) // define NDEBUG to disable assert -# include -# 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* 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* 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 const 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_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 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; - }; - -#endif // _WIIMOTE_H \ No newline at end of file diff --git a/tracker-pt/wiiyourself/wiimote_common.h b/tracker-pt/wiiyourself/wiimote_common.h deleted file mode 100644 index c0fd01e1..00000000 --- a/tracker-pt/wiiyourself/wiimote_common.h +++ /dev/null @@ -1,109 +0,0 @@ -// _______________________________________________________________________________ -// -// - WiiYourself! - native C++ Wiimote library v1.15 RC -// (c) gl.tter 2007-9 - http://gl.tter.org -// -// see License.txt for conditions of use. see History.txt for change log. -// _______________________________________________________________________________ -// -// wiimote_common.h (tab = 4 spaces) - -// 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: - CONNECTED = 1<<0, // wiimote just connected - CONNECTION_LOST = 1<<1, - BATTERY_CHANGED = 1<<2, - BATTERY_DRAINED = 1<<3, // close to empty - LEDS_CHANGED = 1<<4, // (probably redudant as wiimmote never - BUTTONS_CHANGED = 1<<5, // changes them unless requested) - ACCEL_CHANGED = 1<<6, - ORIENTATION_CHANGED = 1<<7, - IR_CHANGED = 1<<8, - // all wiimote flags - WIIMOTE_CHANGED = CONNECTION_LOST|BATTERY_CHANGED|BATTERY_DRAINED| - LEDS_CHANGED|BUTTONS_CHANGED|ACCEL_CHANGED| - ORIENTATION_CHANGED|IR_CHANGED, - // - Extensions -: - // Nunchuk: - 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 (inc. Guitars etc): - 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, - // Balance Board: - BALANCE_CONNECTED = 1<<19, - BALANCE_WEIGHT_CHANGED = 1<<20, - // all flags - BALANCE_CHANGED = BALANCE_CONNECTED|BALANCE_WEIGHT_CHANGED, - - // Motion Plus - MOTIONPLUS_DETECTED = 1<<21, // attached but not enabled - MOTIONPLUS_ENABLED = 1<<22, - MOTIONPLUS_SPEED_CHANGED = 1<<23, - MOTIONPLUS_EXTENSION_CONNECTED = 1<<24, // an extension is found in the - // MotionPlus port - MOTIONPLUS_EXTENSION_DISCONNECTED = 1<<25, // it was disconnected - // all flags - MOTIONPLUS_CHANGED = MOTIONPLUS_DETECTED|MOTIONPLUS_ENABLED| - MOTIONPLUS_SPEED_CHANGED| - MOTIONPLUS_EXTENSION_CONNECTED| - MOTIONPLUS_EXTENSION_DISCONNECTED, - // General: - EXTENSION_DISCONNECTED = 1<<26, - EXTENSION_PARTIALLY_INSERTED = 1<<27, - EXTENSION_CONNECTED = NUNCHUK_CONNECTED|CLASSIC_CONNECTED| - BALANCE_CONNECTED|MOTIONPLUS_ENABLED, - EXTENSION_CHANGED = EXTENSION_DISCONNECTED|NUNCHUK_CHANGED| - CLASSIC_CHANGED|BALANCE_CHANGED|MOTIONPLUS_CHANGED, - // ALL flags: - CHANGED_ALL = WIIMOTE_CHANGED|EXTENSION_CHANGED, - }; diff --git a/tracker-pt/wiiyourself/wiimote_state.h b/tracker-pt/wiiyourself/wiimote_state.h deleted file mode 100644 index 1bf167a2..00000000 --- a/tracker-pt/wiiyourself/wiimote_state.h +++ /dev/null @@ -1,379 +0,0 @@ -// _______________________________________________________________________________ -// -// - 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_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 - -#include "wiimote_common.h" - - -// 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). let me know if your wiimote reports - // a different range. - 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; // read-only (depends on ReportType set) - - struct dot - { - bool bVisible; // other values are not valid if == false - 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<