Wiimoteをメディアアート的に使うプロジェクトなので「WiiMedia」、と名づけたんですが。
もしかしたら任天堂が商標とってるかもしれないなあとか。
http://code.google.com/p/wiimedia/
それはともかく、プログラム群としてはWiimote使いやすくするクラスとか、Virtoolsのプラグインとかが含まれるわけです。
特に、単に加速度センサを取得するだけじゃなくて、モーション認識とか学習アルゴリズムとかを組み込んで、というところを狙ってます。
狙ってますが、なんせLaval Virtualまで5日切ってる訳で、あまり贅沢はいえません。
とにかく動くものを作らねばならないと言う、TGS前のゲームプログラマ状態です。
いや、正確に述べると状況としては「明日、上の人にアルファ版を見せなければならないシステムプログラマ状態」です。
そんなわけでローレイヤーの技術をつついている場合じゃないんですが、
今を逃すとほとんど書くチャンスがなくなると思うので書いておきます。
【現状の問題】
まずは前回の関連アーティクルを読んでください
http://akihiko.ameblo.jp/akihiko/entry-10030158381.html
・BlueSoleilスタックじゃないと駄目だ説
http://www.wiili.org/index.php/Wiimote
→そんなこと無いと思う。現にkako.comのtiny_dllでは通信できてる。
http://www.kako.com/neta/2006-019/2006-019.html
加古さんいわく「普通にHIDと通信したらこうなるはず、この辺参照してください」というところなのですが、
・複数デバイスのサポート
→現状はcWiiMoteクラスが「たぶん動かない」と言ってますが、それを使ったVirtoolsのプラグイン「WiiTools」で使えてます。Wiimoteのインデックスを作ってたくさん開くと言う方法をとっているみたいですが本当に正しいかどうか謎。そもそもハンドルはどうなってるんだとか。
http://simulatedcomicproduct.com/2006/12/cwiimote-02.php
とりあえず加古さんに善意でソースをいただいたので解説。
勘違いがあったらごめんなさい、ご指摘いただければ幸いです。
//tiny_hid_dll.c
//Original code by Kako.com, transfered to Akihiko SHIRAI
#include
//この4つの関数をDLLに書き出します
HANDLE __declspec(dllexport) __stdcall OpenHidHandle(unsigned short vendor_id, unsigned short product_id); void __declspec(dllexport) __stdcall ReadReport(HANDLE handle,unsigned char *InputReport,int *len); void __declspec(dllexport) __stdcall WriteReport(HANDLE handle,unsigned char *OutputReport, int *len); void __declspec(dllexport) __stdcall CloseHidHandle(HANDLE handle);
//デバイスCAPSを取得する関数 NTSTATUS GetDeviceCapabilities( HANDLE ); void PrepareForOverlappedTransfer( void );
HIDP_CAPS Capabilities;
NTSTATUS GetDeviceCapabilities( HANDLE hRsDevHandle ) { PHIDP_PREPARSED_DATA PreparsedData; NTSTATUS Result = HIDP_STATUS_INVALID_PREPARSED_DATA;
if ( HidD_GetPreparsedData( hRsDevHandle, &PreparsedData ) ) { Result = HidP_GetCaps( PreparsedData, &Capabilities ); HidD_FreePreparsedData( PreparsedData ); }
return Result; }
//OpenHidHandle:指定したvendor_id,product_idのハンドルを取得します HANDLE __declspec(dllexport) __stdcall OpenHidHandle(unsigned short vendor_id, unsigned short product_id) { HANDLE hDevHandle; GUID HidGuid; HDEVINFO hDevInfo = 0; BOOL bDevDetected = FALSE; SP_DEVICE_INTERFACE_DATA devInfoData; PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL; HIDD_ATTRIBUTES Attributes; BOOL LastDevice = FALSE; DWORD MemberIndex = 0; LONG Result; DWORD Required; DWORD Length = 0;
hDevHandle = INVALID_HANDLE_VALUE;
HidD_GetHidGuid( &HidGuid ); hDevInfo = SetupDiGetClassDevs( (LPGUID)&HidGuid, NULL, (HWND)NULL, DIGCF_INTERFACEDEVICE | DIGCF_PRESENT ); |
//ここで該当するデバイスが無かったら終わり
if ( 0 == hDevInfo ) return INVALID_HANDLE_VALUE;
//
do { bDevDetected=FALSE; devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
//デバイスの列挙 Result = SetupDiEnumDeviceInterfaces (hDevInfo, NULL, &HidGuid, MemberIndex, //これが気になる。ここではHIDデバイス列挙に使っているが、複数のWiiのサポートが可能なはず &devInfoData);
if (Result != 0) {
//デバイスインタフェース詳細を得るためのメモリ確保
Result = SetupDiGetDeviceInterfaceDetail (hDevInfo, &devInfoData, NULL, 0, &Length, NULL);
detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(Length); detailData -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
//列挙したデバイス個々の詳細を取得 Result = SetupDiGetDeviceInterfaceDetail (hDevInfo, &devInfoData, detailData, Length, &Required, NULL);
//HIDにアクセスするためのファイルを作成しハンドルを取得
hDevHandle = CreateFile (detailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, //ちなみにcWiimoteはここがFILE_FLAG_OVERLAPPEDになってる NULL); |
bDevDetected = FALSE; Attributes.Size = sizeof(Attributes);
//列挙したデバイスのアトリビュートを取得
if ( HidD_GetAttributes( hDevHandle, &Attributes ) ) {
//ベンダーIDとプロダクトIDを取得(ここでProductID == 774, vendor_id = 1406ならWiimote)
if ( Attributes.VendorID == vendor_id && Attributes.ProductID == product_id ) {
//デバイスCapsを取得。この処理はcWiiMoteにはない
if ( HIDP_STATUS_SUCCESS == GetDeviceCapabilities( hDevHandle ) ) {
//Wiimoteゲットだぜ bDevDetected = TRUE; } // End of if ( HIDP_STATUS_SUCCESS == GetDeviceCapabilities( hDevHandle ) )
} else {
//ベンダーID、プロダクトIDが一致しないのでクローズ CloseHandle( hDevHandle ); } // End of if (Attributes.VendorID == RS_VID && Attributes.ProductID == RS_PID)
} else {
//そもそもアトリビュートが取得できないのでクローズ CloseHandle( hDevHandle ); } // End of if ( HidD_GetAttributes( hDevHandle, &Attributes ) )
//詳細データを取得するためのメモリを開放
free(detailData); } else {
//HIDデバイスが列挙できなかった、すなわちもうHIDデバイスが無い LastDevice=TRUE; } //if (Result != 0)
MemberIndex++; //次のHIDデバイスへ(※次のWiimoteではない)
//以後、HIDデバイスが存在し、Wiimoteが見つからないなら繰り返しDo } while ( (LastDevice == FALSE) && (bDevDetected == FALSE) ); //End of do
//複数デバイスをサポートするためには、ここに既に発見したWiimoteをスキップする条件を加えればいいはず。
//もしくはこのOpenHidHandleを再度コールして、既に取得したハンドルならスキップするとか。
//結局デバイスは見つかったのか?
if ( bDevDetected == FALSE ) { hDevHandle = INVALID_HANDLE_VALUE; } // End of (bDevDetected == FALSE)
//掃除
SetupDiDestroyDeviceInfoList(hDevInfo);
return hDevHandle; }
//残りのRead/Write/Close関数はいわゆる低レベルAPIをそのまま使ってる、ただしnNumberOfBytesToRead/WriteをデバイスCapsのByteLengthにしているようだ。
void __declspec(dllexport) __stdcall ReadReport(HANDLE handle,unsigned char *InputReport,int *len) { ReadFile(handle,InputReport,Capabilities.InputReportByteLength,len,NULL); }
void __declspec(dllexport) __stdcall WriteReport(HANDLE handle , unsigned char *OutputReport, int *len) { WriteFile(handle,OutputReport,Capabilities.OutputReportByteLength,len,NULL); }
void __declspec(dllexport) __stdcall CloseHidHandle( HANDLE handle ) { CloseHandle( handle ); }
ちなみにWiiLiではRobert Marquardtという方が「HidD_SetOutputReportを使え!」というの指摘をしているが、実際SetOutputReportも、上記WriteFileのCapsのBytelengthを与えても結果は変わらなかった…。
自分が試しているソースがVirtools用だからかもしれない。
うまく動いているかどうかもよくわからないSingletonクラス(3nouMAXという人)がクラス化→BB化したのを流用しているのがまずいけない。
もっとシンプルなコードで実験するべきだろう。kakoさんのデモは動いているわけだし。
それにしても、あと5日で結果を得なければならない立場としてはつらいところだな。