#include <wiimote.h>
Classes | |
struct | hid |
struct | recording |
Public Types | |
enum | input_report { IN_BUTTONS = 0x30, IN_BUTTONS_ACCEL = 0x31, IN_BUTTONS_ACCEL_IR = 0x33, IN_BUTTONS_ACCEL_EXT = 0x35, IN_BUTTONS_ACCEL_IR_EXT = 0x37, IN_BUTTONS_BALANCE_BOARD = 0x32 } |
typedef wiimote_state_event | state_event |
typedef std::list< state_event > | state_history |
Public Member Functions | |
wiimote () | |
virtual | ~wiimote () |
bool | operator== (const wiimote &remote) |
bool | operator!= (const wiimote &remote) |
bool | IsConnected () const |
bool | ConnectionLost () const |
bool | IsBalanceBoard () const |
bool | NunchukConnected () const |
bool | ClassicConnected () const |
bool | MotionPlusConnected () const |
bool | MotionPlusEnabled () const |
bool | MotionPlusHasExtension () const |
bool | IsPlayingAudio () const |
bool | IsPlayingSample () const |
bool | IsUsingHIDwrites () const |
bool | IsRecordingState () const |
virtual void | ChangedNotifier (state_change_flags changed, const wiimote_state &new_state) |
bool | Connect (unsigned wiimote_index=FIRST_AVAILABLE, bool force_hidwrites=false) |
void | Disconnect () |
void | SetReportType (input_report type, bool continuous=false) |
bool | EnableMotionPlus () |
bool | DisableMotionPlus () |
void | CalibrateAtRest () |
state_change_flags | RefreshState () |
void | Reset () |
void | SetLEDs (BYTE led_bits) |
void | SetRumble (bool on) |
void | RumbleForAsync (unsigned milliseconds) |
bool | MuteSpeaker (bool on) |
bool | EnableSpeaker (bool on) |
bool | PlaySquareWave (speaker_freq freq, BYTE volume=0x40) |
bool | PlaySample (const wiimote_sample &sample, BYTE volume=0x40, speaker_freq freq_override=FREQ_NONE) |
void | RecordState (state_history &events_out, unsigned max_time_ms=UNTIL_STOP, state_change_flags change_trigger=CHANGED_ALL) |
void | StopRecording () |
Static Public Member Functions | |
static unsigned | TotalConnected () |
static const TCHAR * | GetButtonNameFromBit (unsigned index) |
static const TCHAR * | GetClassicButtonNameFromBit (unsigned index) |
static const unsigned | GetFreqLookup (unsigned index) |
static bool | Load16bitMonoSampleWAV (const TCHAR *filepath, wiimote_sample &out) |
static bool | Load16BitMonoSampleRAW (const TCHAR *filepath, bool _signed, speaker_freq freq, wiimote_sample &out) |
static bool | Convert16bitMonoSamples (const short *samples, bool _signed, DWORD length, speaker_freq freq, wiimote_sample &out) |
Public Attributes | |
QWORD | UniqueID |
state_changed_callback | ChangedCallback |
state_change_flags | CallbackTriggerFlags |
Static Public Attributes | |
static const TCHAR * | ReportTypeName [] |
static const TCHAR * | ButtonNameFromBit [16] |
static const TCHAR * | ClassicButtonNameFromBit [16] |
static const unsigned | FreqLookup [10] |
static const unsigned | FIRST_AVAILABLE = 0xffffffff |
static const unsigned | UNTIL_STOP = 0xffffffff |
Definition at line 92 of file wiimote.h.
typedef std::list<state_event> wiimote::state_history |
IN_BUTTONS | |
IN_BUTTONS_ACCEL | |
IN_BUTTONS_ACCEL_IR | |
IN_BUTTONS_ACCEL_EXT | |
IN_BUTTONS_ACCEL_IR_EXT | |
IN_BUTTONS_BALANCE_BOARD |
Definition at line 108 of file wiimote.h.
{ // 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 };
wiimote::wiimote | ( | ) |
Definition at line 129 of file wiimote.cpp.
References _ASSERT, wiimote_state::Clear(), INVALID_HANDLE_VALUE, TRACE, and WARN.
: 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 | ( | ) | [virtual] |
Definition at line 199 of file wiimote.cpp.
References Disconnect(), and INVALID_HANDLE_VALUE.
{ 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; } }
void wiimote::CalibrateAtRest | ( | ) |
Definition at line 467 of file wiimote.cpp.
References _ASSERT, wiimote_state::balance_board::AtRestKg, wiimote_state::BalanceBoard, IsBalanceBoard(), IsConnected(), wiimote_state::balance_board::Kg, RefreshState(), and TRACE.
{ _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(); } }
virtual void wiimote::ChangedNotifier | ( | state_change_flags | changed, |
const wiimote_state & | new_state | ||
) | [inline, virtual] |
bool wiimote::ClassicConnected | ( | ) | const [inline] |
Definition at line 130 of file wiimote.h.
References wiimote_state::bExtension, wiimote_state::CLASSIC, and wiimote_state::ExtensionType.
{ return (Internal.bExtension && (Internal.ExtensionType==wiimote_state::CLASSIC)); }
bool wiimote::Connect | ( | unsigned | wiimote_index = FIRST_AVAILABLE , |
bool | force_hidwrites = false |
||
) |
Definition at line 224 of file wiimote.cpp.
References _ASSERT, wiimote_state::CalibrationInfo, CallbackTriggerFlags, ChangedCallback, ChangedNotifier(), wiimote_state::Clear(), CONNECTED, DEEP_TRACE, DETECT_MPLUS_COUNT, DETECT_MPLUS_EVERY_MS, Disconnect(), FIRST_AVAILABLE, INVALID_HANDLE_VALUE, IsConnected(), NO_CHANGE, RefreshState(), REQUEST_STATUS_EVERY_MS, Reset(), TRACE, UniqueID, and WARN.
{ 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, 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; }
bool wiimote::ConnectionLost | ( | ) | const [inline] |
bool wiimote::Convert16bitMonoSamples | ( | const short * | samples, |
bool | _signed, | ||
DWORD | length, | ||
speaker_freq | freq, | ||
wiimote_sample & | out | ||
) | [static] |
Definition at line 2594 of file wiimote.cpp.
References _ASSERT, wiimote_sample::freq, wiimote_sample::length, and wiimote_sample::samples.
Referenced by Load16BitMonoSampleRAW(), and Load16bitMonoSampleWAV().
{ // 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<length; i++) { // convert to 16bit signed int value = samples[i];// (8bit) << 8);// | samples[i]; // dither it? if(!_signed) value -= 32768; // encode: int diff = value - adpcm_prev_value; BYTE encoded_val = 0; if(diff < 0) { encoded_val |= 8; diff = -diff; } diff = (diff << 2) / adpcm_step; if (diff > 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::DisableMotionPlus | ( | ) |
Definition at line 1086 of file wiimote.cpp.
References TRACE.
Referenced by Reset().
{ 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::Disconnect | ( | ) |
Definition at line 482 of file wiimote.cpp.
References _ASSERT, wiimote_state::Clear(), INVALID_HANDLE_VALUE, IsConnected(), NO_CHANGE, Reset(), TRACE, and UniqueID.
Referenced by Connect(), and ~wiimote().
{ 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; }
bool wiimote::EnableMotionPlus | ( | ) |
Definition at line 1062 of file wiimote.cpp.
References _ASSERT, and TRACE.
{ _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::EnableSpeaker | ( | bool | on ) |
Definition at line 2208 of file wiimote.cpp.
References _ASSERT, wiimote_state::speaker::bEnabled, wiimote_state::speaker::Freq, FREQ_NONE, IsConnected(), MuteSpeaker(), wiimote_state::Speaker, TRACE, and wiimote_state::speaker::Volume.
Referenced by PlaySample(), PlaySquareWave(), and Reset().
{ _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; }
static const TCHAR* wiimote::GetButtonNameFromBit | ( | unsigned | index ) | [inline, static] |
Definition at line 169 of file wiimote.h.
References _ASSERT, ButtonNameFromBit, and TOTAL_BUTTON_BITS.
{ _ASSERT(index < TOTAL_BUTTON_BITS); if(index >= TOTAL_BUTTON_BITS) return _T("[invalid index]"); return ButtonNameFromBit[index]; }
static const TCHAR* wiimote::GetClassicButtonNameFromBit | ( | unsigned | index ) | [inline, static] |
Definition at line 179 of file wiimote.h.
References _ASSERT, ClassicButtonNameFromBit, and TOTAL_BUTTON_BITS.
{ _ASSERT(index < TOTAL_BUTTON_BITS); if(index >= TOTAL_BUTTON_BITS) return _T("[invalid index]"); return ClassicButtonNameFromBit[index]; }
static const unsigned wiimote::GetFreqLookup | ( | unsigned | index ) | [inline, static] |
Definition at line 189 of file wiimote.h.
References _ASSERT, FreqLookup, and TOTAL_FREQUENCIES.
{ _ASSERT(index < TOTAL_FREQUENCIES); if(index >= TOTAL_FREQUENCIES) return 0; return FreqLookup[index]; }
bool wiimote::IsBalanceBoard | ( | ) | const [inline] |
Definition at line 126 of file wiimote.h.
References wiimote_state::BALANCE_BOARD, wiimote_state::bExtension, and wiimote_state::ExtensionType.
Referenced by CalibrateAtRest(), Reset(), and SetReportType().
{ return (Internal.bExtension && (Internal.ExtensionType==wiimote_state::BALANCE_BOARD)); }
bool wiimote::IsConnected | ( | ) | const [inline] |
Definition at line 123 of file wiimote.h.
Referenced by CalibrateAtRest(), Connect(), Disconnect(), EnableSpeaker(), MuteSpeaker(), PlaySample(), PlaySquareWave(), RumbleForAsync(), SetLEDs(), SetReportType(), and SetRumble().
{ return bStatusReceived; }
bool wiimote::IsPlayingAudio | ( | ) | const [inline] |
Definition at line 135 of file wiimote.h.
References wiimote_state::speaker::Freq, wiimote_state::Speaker, and wiimote_state::speaker::Volume.
Referenced by IsPlayingSample(), PlaySquareWave(), and SetRumble().
bool wiimote::IsPlayingSample | ( | ) | const [inline] |
Definition at line 137 of file wiimote.h.
References IsPlayingAudio().
Referenced by PlaySquareWave().
{ return IsPlayingAudio() && (CurrentSample != NULL); }
bool wiimote::IsRecordingState | ( | ) | const [inline] |
bool wiimote::IsUsingHIDwrites | ( | ) | const [inline] |
bool wiimote::Load16BitMonoSampleRAW | ( | const TCHAR * | filepath, |
bool | _signed, | ||
speaker_freq | freq, | ||
wiimote_sample & | out | ||
) | [static] |
Definition at line 2530 of file wiimote.cpp.
References _ASSERT, Convert16bitMonoSamples(), TRACE, and WARN.
{ // 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::Load16bitMonoSampleWAV | ( | const TCHAR * | filepath, |
wiimote_sample & | out | ||
) | [static] |
Definition at line 2362 of file wiimote.cpp.
References _ASSERT, ARRAY_ENTRIES, Convert16bitMonoSamples(), FREQ_NONE, FreqLookup, READ, READ_SIZE, TRACE, and WARN.
{ // 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<ARRAY_ENTRIES(FreqLookup); index++) { if((sample_freq+epsilon) >= 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::MotionPlusConnected | ( | ) | const [inline] |
bool wiimote::MotionPlusEnabled | ( | ) | const [inline] |
bool wiimote::MotionPlusHasExtension | ( | ) | const [inline] |
bool wiimote::MuteSpeaker | ( | bool | on ) |
Definition at line 2186 of file wiimote.cpp.
References _ASSERT, wiimote_state::speaker::bMuted, IsConnected(), wiimote_state::Speaker, and TRACE.
Referenced by EnableSpeaker(), PlaySample(), and PlaySquareWave().
{ _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::NunchukConnected | ( | ) | const [inline] |
Definition at line 128 of file wiimote.h.
References wiimote_state::bExtension, wiimote_state::ExtensionType, and wiimote_state::NUNCHUK.
{ return (Internal.bExtension && (Internal.ExtensionType==wiimote_state::NUNCHUK)); }
bool wiimote::operator!= | ( | const wiimote & | remote ) | [inline] |
bool wiimote::operator== | ( | const wiimote & | remote ) | [inline] |
bool wiimote::PlaySample | ( | const wiimote_sample & | sample, |
BYTE | volume = 0x40 , |
||
speaker_freq | freq_override = FREQ_NONE |
||
) |
Definition at line 2669 of file wiimote.cpp.
References _ASSERT, EnableSpeaker(), wiimote_state::speaker::Freq, wiimote_sample::freq, HANDLE(), IsConnected(), MuteSpeaker(), wiimote_state::Speaker, TRACE, wiimote_state::speaker::Volume, WARN, and WORKER_THREAD_PRIORITY.
{ _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::PlaySquareWave | ( | speaker_freq | freq, |
BYTE | volume = 0x40 |
||
) |
Definition at line 2725 of file wiimote.cpp.
References _ASSERT, EnableSpeaker(), wiimote_state::speaker::Freq, IsConnected(), IsPlayingAudio(), IsPlayingSample(), MuteSpeaker(), wiimote_state::Speaker, TRACE, and wiimote_state::speaker::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 = UNTIL_STOP , |
||
state_change_flags | change_trigger = CHANGED_ALL |
||
) |
Definition at line 2770 of file wiimote.cpp.
References StopRecording().
{ // 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; }
state_change_flags wiimote::RefreshState | ( | ) |
Definition at line 1016 of file wiimote.cpp.
References wiimote_state::ClassicController, wiimote_state::joystick::DeadZone, wiimote_state::nunchuk::Joystick, wiimote_state::classic_controller::JoystickL, wiimote_state::classic_controller::JoystickR, NO_CHANGE, and wiimote_state::Nunchuk.
Referenced by CalibrateAtRest(), and Connect().
{ // 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::Reset | ( | ) |
Definition at line 539 of file wiimote.cpp.
References DisableMotionPlus(), EnableSpeaker(), IN_BUTTONS, IN_BUTTONS_BALANCE_BOARD, IsBalanceBoard(), SetLEDs(), SetReportType(), SetRumble(), and TRACE.
Referenced by Connect(), and Disconnect().
{ 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() }
void wiimote::RumbleForAsync | ( | unsigned | milliseconds ) |
Definition at line 843 of file wiimote.cpp.
References _ASSERT, HANDLE(), IsConnected(), SetRumble(), WARN, and WORKER_THREAD_PRIORITY.
{ // 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::SetLEDs | ( | BYTE | led_bits ) |
Definition at line 779 of file wiimote.cpp.
References _ASSERT, wiimote_state::leds::Bits, IsConnected(), and wiimote_state::LED.
Referenced by Reset().
{ _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::SetReportType | ( | input_report | type, |
bool | continuous = false |
||
) |
Definition at line 734 of file wiimote.cpp.
References _ASSERT, wiimote_state::ir::BASIC, wiimote_state::ir::EXTENDED, IN_BUTTONS, IN_BUTTONS_ACCEL_EXT, IN_BUTTONS_ACCEL_IR, IN_BUTTONS_ACCEL_IR_EXT, IN_BUTTONS_BALANCE_BOARD, IsBalanceBoard(), IsConnected(), TRACE, and TYPE2NAME.
Referenced by Reset().
{ _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::SetRumble | ( | bool | on ) |
Definition at line 796 of file wiimote.cpp.
References _ASSERT, wiimote_state::bRumble, IsConnected(), and IsPlayingAudio().
Referenced by Reset(), and RumbleForAsync().
{ _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); }
void wiimote::StopRecording | ( | ) |
Definition at line 2795 of file wiimote.cpp.
Referenced by RecordState().
{ 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? }
static unsigned wiimote::TotalConnected | ( | ) | [inline, static] |
const TCHAR * wiimote::ButtonNameFromBit [static] |
{ _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") }
Definition at line 165 of file wiimote.h.
Referenced by GetButtonNameFromBit().
Definition at line 162 of file wiimote.h.
Referenced by Connect(), and mitk::WiiMoteThread::WiiMoteThread().
Definition at line 159 of file wiimote.h.
Referenced by Connect(), and mitk::WiiMoteThread::WiiMoteThread().
const TCHAR * wiimote::ClassicButtonNameFromBit [static] |
{ _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") }
Definition at line 178 of file wiimote.h.
Referenced by GetClassicButtonNameFromBit().
const unsigned wiimote::FIRST_AVAILABLE = 0xffffffff [static] |
Definition at line 205 of file wiimote.h.
Referenced by Connect(), mitk::WiiMoteThread::ReconnectWiiMote(), and mitk::WiiMoteThread::StartWiiMote().
const unsigned wiimote::FreqLookup [static] |
{ 0, 4200, 3920, 3640, 3360, 3130, 2940, 2760, 2610, 2470 }
Definition at line 188 of file wiimote.h.
Referenced by GetFreqLookup(), and Load16bitMonoSampleWAV().
const TCHAR* wiimote::ReportTypeName[] [static] |
Definition at line 146 of file wiimote.h.
Referenced by Connect(), and Disconnect().
const unsigned wiimote::UNTIL_STOP = 0xffffffff [static] |